Merge branch 'gh-34657'
Closes gh-34657
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.actuate.autoconfigure.tracing.zipkin;
|
||||
|
||||
/**
|
||||
* Adapts {@link ZipkinProperties} to {@link ZipkinConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class PropertiesZipkinConnectionDetails implements ZipkinConnectionDetails {
|
||||
|
||||
private final ZipkinProperties properties;
|
||||
|
||||
PropertiesZipkinConnectionDetails(ZipkinProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpanEndpoint() {
|
||||
return this.properties.getEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -59,11 +59,14 @@ class ZipkinConfigurations {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Sender.class)
|
||||
URLConnectionSender urlConnectionSender(ZipkinProperties properties) {
|
||||
URLConnectionSender urlConnectionSender(ZipkinProperties properties,
|
||||
ObjectProvider<ZipkinConnectionDetails> connectionDetailsProvider) {
|
||||
ZipkinConnectionDetails connectionDetails = connectionDetailsProvider
|
||||
.getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties));
|
||||
URLConnectionSender.Builder builder = URLConnectionSender.newBuilder();
|
||||
builder.connectTimeout((int) properties.getConnectTimeout().toMillis());
|
||||
builder.readTimeout((int) properties.getReadTimeout().toMillis());
|
||||
builder.endpoint(properties.getEndpoint());
|
||||
builder.endpoint(connectionDetails.getSpanEndpoint());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -77,12 +80,15 @@ class ZipkinConfigurations {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Sender.class)
|
||||
ZipkinRestTemplateSender restTemplateSender(ZipkinProperties properties,
|
||||
ObjectProvider<ZipkinRestTemplateBuilderCustomizer> customizers) {
|
||||
ObjectProvider<ZipkinRestTemplateBuilderCustomizer> customizers,
|
||||
ObjectProvider<ZipkinConnectionDetails> connectionDetailsProvider) {
|
||||
ZipkinConnectionDetails connectionDetails = connectionDetailsProvider
|
||||
.getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties));
|
||||
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder()
|
||||
.setConnectTimeout(properties.getConnectTimeout())
|
||||
.setReadTimeout(properties.getReadTimeout());
|
||||
restTemplateBuilder = applyCustomizers(restTemplateBuilder, customizers);
|
||||
return new ZipkinRestTemplateSender(properties.getEndpoint(), restTemplateBuilder.build());
|
||||
return new ZipkinRestTemplateSender(connectionDetails.getSpanEndpoint(), restTemplateBuilder.build());
|
||||
}
|
||||
|
||||
private RestTemplateBuilder applyCustomizers(RestTemplateBuilder restTemplateBuilder,
|
||||
@@ -106,10 +112,13 @@ class ZipkinConfigurations {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Sender.class)
|
||||
ZipkinWebClientSender webClientSender(ZipkinProperties properties,
|
||||
ObjectProvider<ZipkinWebClientBuilderCustomizer> customizers) {
|
||||
ObjectProvider<ZipkinWebClientBuilderCustomizer> customizers,
|
||||
ObjectProvider<ZipkinConnectionDetails> connectionDetailsProvider) {
|
||||
ZipkinConnectionDetails connectionDetails = connectionDetailsProvider
|
||||
.getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties));
|
||||
WebClient.Builder builder = WebClient.builder();
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return new ZipkinWebClientSender(properties.getEndpoint(), builder.build());
|
||||
return new ZipkinWebClientSender(connectionDetails.getSpanEndpoint(), builder.build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.actuate.autoconfigure.tracing.zipkin;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to a Zipkin server.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface ZipkinConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* The endpoint for the span reporting.
|
||||
* @return the endpoint
|
||||
*/
|
||||
String getSpanEndpoint();
|
||||
|
||||
}
|
||||
@@ -236,6 +236,8 @@ dependencies {
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testImplementation("org.mockito:mockito-core")
|
||||
testImplementation("org.mockito:mockito-junit-jupiter")
|
||||
testImplementation("org.postgresql:postgresql")
|
||||
testImplementation("org.postgresql:r2dbc-postgresql")
|
||||
testImplementation("org.skyscreamer:jsonassert")
|
||||
testImplementation("org.springframework:spring-test")
|
||||
testImplementation("org.springframework:spring-core-test")
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.amqp;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.amqp.rabbit.connection.AbstractConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionNameStrategy;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
@@ -27,6 +29,9 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @param <T> the connection factory type.
|
||||
* @author Chris Bono
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public abstract class AbstractConnectionFactoryConfigurer<T extends AbstractConnectionFactory> {
|
||||
@@ -35,9 +40,31 @@ public abstract class AbstractConnectionFactoryConfigurer<T extends AbstractConn
|
||||
|
||||
private ConnectionNameStrategy connectionNameStrategy;
|
||||
|
||||
private final RabbitConnectionDetails connectionDetails;
|
||||
|
||||
/**
|
||||
* Creates a new configurer that will configure the connection factory using the given
|
||||
* {@code properties}.
|
||||
* @param properties the properties to use to configure the connection factory
|
||||
*/
|
||||
protected AbstractConnectionFactoryConfigurer(RabbitProperties properties) {
|
||||
Assert.notNull(properties, "RabbitProperties must not be null");
|
||||
this(properties, new PropertiesRabbitConnectionDetails(properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new configurer that will configure the connection factory using the given
|
||||
* {@code properties} and {@code connectionDetails}, with the latter taking priority.
|
||||
* @param properties the properties to use to configure the connection factory
|
||||
* @param connectionDetails the connection details to use to configure the connection
|
||||
* factory
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected AbstractConnectionFactoryConfigurer(RabbitProperties properties,
|
||||
RabbitConnectionDetails connectionDetails) {
|
||||
Assert.notNull(properties, "Properties must not be null");
|
||||
Assert.notNull(connectionDetails, "ConnectionDetails must not be null");
|
||||
this.rabbitProperties = properties;
|
||||
this.connectionDetails = connectionDetails;
|
||||
}
|
||||
|
||||
protected final ConnectionNameStrategy getConnectionNameStrategy() {
|
||||
@@ -55,7 +82,11 @@ public abstract class AbstractConnectionFactoryConfigurer<T extends AbstractConn
|
||||
public final void configure(T connectionFactory) {
|
||||
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
map.from(this.rabbitProperties::determineAddresses).to(connectionFactory::setAddresses);
|
||||
String addresses = this.connectionDetails.getAddresses()
|
||||
.stream()
|
||||
.map((address) -> address.host() + ":" + address.port())
|
||||
.collect(Collectors.joining(","));
|
||||
map.from(addresses).to(connectionFactory::setAddresses);
|
||||
map.from(this.rabbitProperties::getAddressShuffleMode)
|
||||
.whenNonNull()
|
||||
.to(connectionFactory::setAddressShuffleMode);
|
||||
|
||||
@@ -25,12 +25,32 @@ import org.springframework.boot.context.properties.PropertyMapper;
|
||||
* Configures Rabbit {@link CachingConnectionFactory} with sensible defaults.
|
||||
*
|
||||
* @author Chris Bono
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public class CachingConnectionFactoryConfigurer extends AbstractConnectionFactoryConfigurer<CachingConnectionFactory> {
|
||||
|
||||
/**
|
||||
* Creates a new configurer that will configure the connection factory using the given
|
||||
* {@code properties}.
|
||||
* @param properties the properties to use to configure the connection factory
|
||||
*/
|
||||
public CachingConnectionFactoryConfigurer(RabbitProperties properties) {
|
||||
super(properties);
|
||||
this(properties, new PropertiesRabbitConnectionDetails(properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new configurer that will configure the connection factory using the given
|
||||
* {@code properties} and {@code connectionDetails}, with the latter taking priority.
|
||||
* @param properties the properties to use to configure the connection factory
|
||||
* @param connectionDetails the connection details to use to configure the connection
|
||||
* factory
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public CachingConnectionFactoryConfigurer(RabbitProperties properties, RabbitConnectionDetails connectionDetails) {
|
||||
super(properties, connectionDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.amqp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Adapts {@link RabbitProperties} to {@link RabbitConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class PropertiesRabbitConnectionDetails implements RabbitConnectionDetails {
|
||||
|
||||
private final RabbitProperties properties;
|
||||
|
||||
public PropertiesRabbitConnectionDetails(RabbitProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.properties.determineUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.properties.determinePassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVirtualHost() {
|
||||
return this.properties.determineVirtualHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Address> getAddresses() {
|
||||
List<Address> addresses = new ArrayList<>();
|
||||
for (String address : this.properties.determineAddresses().split(",")) {
|
||||
String[] components = address.split(":");
|
||||
addresses.add(new Address(components[0], Integer.parseInt(components[1])));
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -59,21 +59,6 @@ import org.springframework.core.io.ResourceLoader;
|
||||
* <li>{@link org.springframework.amqp.core.AmqpAdmin } instance as long as
|
||||
* {@literal spring.rabbitmq.dynamic=true}.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* The {@link org.springframework.amqp.rabbit.connection.CachingConnectionFactory} honors
|
||||
* the following properties:
|
||||
* <ul>
|
||||
* <li>{@literal spring.rabbitmq.port} is used to specify the port to which the client
|
||||
* should connect, and defaults to 5672.</li>
|
||||
* <li>{@literal spring.rabbitmq.username} is used to specify the (optional) username.
|
||||
* </li>
|
||||
* <li>{@literal spring.rabbitmq.password} is used to specify the (optional) password.
|
||||
* </li>
|
||||
* <li>{@literal spring.rabbitmq.host} is used to specify the host, and defaults to
|
||||
* {@literal localhost}.</li>
|
||||
* <li>{@literal spring.rabbitmq.virtualHost} is used to specify the (optional) virtual
|
||||
* host to which the client should connect.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Josh Long
|
||||
@@ -82,6 +67,8 @@ import org.springframework.core.io.ResourceLoader;
|
||||
* @author Phillip Webb
|
||||
* @author Artsiom Yudovin
|
||||
* @author Chris Bono
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@@ -93,13 +80,24 @@ public class RabbitAutoConfiguration {
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
protected static class RabbitConnectionFactoryCreator {
|
||||
|
||||
private final RabbitProperties properties;
|
||||
|
||||
private final RabbitConnectionDetails connectionDetails;
|
||||
|
||||
protected RabbitConnectionFactoryCreator(RabbitProperties properties,
|
||||
ObjectProvider<RabbitConnectionDetails> connectionDetails) {
|
||||
this.properties = properties;
|
||||
this.connectionDetails = connectionDetails
|
||||
.getIfAvailable(() -> new PropertiesRabbitConnectionDetails(properties));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
RabbitConnectionFactoryBeanConfigurer rabbitConnectionFactoryBeanConfigurer(RabbitProperties properties,
|
||||
ResourceLoader resourceLoader, ObjectProvider<CredentialsProvider> credentialsProvider,
|
||||
RabbitConnectionFactoryBeanConfigurer rabbitConnectionFactoryBeanConfigurer(ResourceLoader resourceLoader,
|
||||
ObjectProvider<CredentialsProvider> credentialsProvider,
|
||||
ObjectProvider<CredentialsRefreshService> credentialsRefreshService) {
|
||||
RabbitConnectionFactoryBeanConfigurer configurer = new RabbitConnectionFactoryBeanConfigurer(resourceLoader,
|
||||
properties);
|
||||
this.properties, this.connectionDetails);
|
||||
configurer.setCredentialsProvider(credentialsProvider.getIfUnique());
|
||||
configurer.setCredentialsRefreshService(credentialsRefreshService.getIfUnique());
|
||||
return configurer;
|
||||
@@ -107,9 +105,10 @@ public class RabbitAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
CachingConnectionFactoryConfigurer rabbitConnectionFactoryConfigurer(RabbitProperties rabbitProperties,
|
||||
CachingConnectionFactoryConfigurer rabbitConnectionFactoryConfigurer(
|
||||
ObjectProvider<ConnectionNameStrategy> connectionNameStrategy) {
|
||||
CachingConnectionFactoryConfigurer configurer = new CachingConnectionFactoryConfigurer(rabbitProperties);
|
||||
CachingConnectionFactoryConfigurer configurer = new CachingConnectionFactoryConfigurer(this.properties,
|
||||
this.connectionDetails);
|
||||
configurer.setConnectionNameStrategy(connectionNameStrategy.getIfUnique());
|
||||
return configurer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.amqp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to a RabbitMQ service.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface RabbitConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* Login user to authenticate to the broker.
|
||||
* @return the login user to authenticate to the broker or {@code null}
|
||||
*/
|
||||
default String getUsername() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login to authenticate against the broker.
|
||||
* @return the login to authenticate against the broker or {@code null}
|
||||
*/
|
||||
default String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Virtual host to use when connecting to the broker.
|
||||
* @return the virtual host to use when connecting to the broker or {@code null}
|
||||
*/
|
||||
default String getVirtualHost() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of addresses to which the client should connect. Must return at least one
|
||||
* address.
|
||||
* @return the list of addresses to which the client should connect
|
||||
*/
|
||||
List<Address> getAddresses();
|
||||
|
||||
/**
|
||||
* Returns the first address.
|
||||
* @return the first address
|
||||
* @throws IllegalStateException if the address list is empty
|
||||
*/
|
||||
default Address getFirstAddress() {
|
||||
List<Address> addresses = getAddresses();
|
||||
Assert.state(!addresses.isEmpty(), "Address list is empty");
|
||||
return addresses.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* A RabbitMQ address.
|
||||
*
|
||||
* @param host the host
|
||||
* @param port the port
|
||||
*/
|
||||
record Address(String host, int port) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import com.rabbitmq.client.impl.CredentialsProvider;
|
||||
import com.rabbitmq.client.impl.CredentialsRefreshService;
|
||||
|
||||
import org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitConnectionDetails.Address;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -30,6 +31,9 @@ import org.springframework.util.Assert;
|
||||
* Configures {@link RabbitConnectionFactoryBean} with sensible defaults.
|
||||
*
|
||||
* @author Chris Bono
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public class RabbitConnectionFactoryBeanConfigurer {
|
||||
@@ -38,13 +42,39 @@ public class RabbitConnectionFactoryBeanConfigurer {
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
private final RabbitConnectionDetails connectionDetails;
|
||||
|
||||
private CredentialsProvider credentialsProvider;
|
||||
|
||||
private CredentialsRefreshService credentialsRefreshService;
|
||||
|
||||
/**
|
||||
* Creates a new configurer that will use the given {@code resourceLoader} and
|
||||
* {@code properties}.
|
||||
* @param resourceLoader the resource loader
|
||||
* @param properties the properties
|
||||
*/
|
||||
public RabbitConnectionFactoryBeanConfigurer(ResourceLoader resourceLoader, RabbitProperties properties) {
|
||||
this(resourceLoader, properties, new PropertiesRabbitConnectionDetails(properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new configurer that will use the given {@code resourceLoader},
|
||||
* {@code properties}, and {@code connectionDetails}. The connection details have
|
||||
* priority over the properties.
|
||||
* @param resourceLoader the resource loader
|
||||
* @param properties the properties
|
||||
* @param connectionDetails the connection details.
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public RabbitConnectionFactoryBeanConfigurer(ResourceLoader resourceLoader, RabbitProperties properties,
|
||||
RabbitConnectionDetails connectionDetails) {
|
||||
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
|
||||
Assert.notNull(properties, "Properties must not be null");
|
||||
Assert.notNull(connectionDetails, "ConnectionDetails must not be null");
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.rabbitProperties = properties;
|
||||
this.connectionDetails = connectionDetails;
|
||||
}
|
||||
|
||||
public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
|
||||
@@ -65,12 +95,13 @@ public class RabbitConnectionFactoryBeanConfigurer {
|
||||
public void configure(RabbitConnectionFactoryBean factory) {
|
||||
Assert.notNull(factory, "RabbitConnectionFactoryBean must not be null");
|
||||
factory.setResourceLoader(this.resourceLoader);
|
||||
Address address = this.connectionDetails.getFirstAddress();
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
map.from(this.rabbitProperties::determineHost).whenNonNull().to(factory::setHost);
|
||||
map.from(this.rabbitProperties::determinePort).to(factory::setPort);
|
||||
map.from(this.rabbitProperties::determineUsername).whenNonNull().to(factory::setUsername);
|
||||
map.from(this.rabbitProperties::determinePassword).whenNonNull().to(factory::setPassword);
|
||||
map.from(this.rabbitProperties::determineVirtualHost).whenNonNull().to(factory::setVirtualHost);
|
||||
map.from(address::host).whenNonNull().to(factory::setHost);
|
||||
map.from(address::port).to(factory::setPort);
|
||||
map.from(this.connectionDetails::getUsername).whenNonNull().to(factory::setUsername);
|
||||
map.from(this.connectionDetails::getPassword).whenNonNull().to(factory::setPassword);
|
||||
map.from(this.connectionDetails::getVirtualHost).whenNonNull().to(factory::setVirtualHost);
|
||||
map.from(this.rabbitProperties::getRequestedHeartbeat)
|
||||
.whenNonNull()
|
||||
.asInt(Duration::getSeconds)
|
||||
|
||||
@@ -23,7 +23,6 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
@@ -64,10 +63,13 @@ import org.springframework.core.io.Resource;
|
||||
* @author Stephane Nicoll
|
||||
* @author Steffen F. Qvistgaard
|
||||
* @author Ittay Stern
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass({ CqlSession.class })
|
||||
@ConditionalOnClass(CqlSession.class)
|
||||
@EnableConfigurationProperties(CassandraProperties.class)
|
||||
public class CassandraAutoConfiguration {
|
||||
|
||||
@@ -80,6 +82,17 @@ public class CassandraAutoConfiguration {
|
||||
SPRING_BOOT_DEFAULTS = options.build();
|
||||
}
|
||||
|
||||
private final CassandraProperties properties;
|
||||
|
||||
private final CassandraConnectionDetails connectionDetails;
|
||||
|
||||
CassandraAutoConfiguration(CassandraProperties properties,
|
||||
ObjectProvider<CassandraConnectionDetails> connectionDetails) {
|
||||
this.properties = properties;
|
||||
this.connectionDetails = connectionDetails
|
||||
.getIfAvailable(() -> new PropertiesCassandraConnectionDetails(properties));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Lazy
|
||||
@@ -90,24 +103,25 @@ public class CassandraAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Scope("prototype")
|
||||
public CqlSessionBuilder cassandraSessionBuilder(CassandraProperties properties,
|
||||
DriverConfigLoader driverConfigLoader, ObjectProvider<CqlSessionBuilderCustomizer> builderCustomizers) {
|
||||
public CqlSessionBuilder cassandraSessionBuilder(DriverConfigLoader driverConfigLoader,
|
||||
ObjectProvider<CqlSessionBuilderCustomizer> builderCustomizers) {
|
||||
CqlSessionBuilder builder = CqlSession.builder().withConfigLoader(driverConfigLoader);
|
||||
configureAuthentication(properties, builder);
|
||||
configureSsl(properties, builder);
|
||||
builder.withKeyspace(properties.getKeyspaceName());
|
||||
configureAuthentication(builder);
|
||||
configureSsl(builder);
|
||||
builder.withKeyspace(this.properties.getKeyspaceName());
|
||||
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder;
|
||||
}
|
||||
|
||||
private void configureAuthentication(CassandraProperties properties, CqlSessionBuilder builder) {
|
||||
if (properties.getUsername() != null) {
|
||||
builder.withAuthCredentials(properties.getUsername(), properties.getPassword());
|
||||
private void configureAuthentication(CqlSessionBuilder builder) {
|
||||
String username = this.connectionDetails.getUsername();
|
||||
if (username != null) {
|
||||
builder.withAuthCredentials(username, this.connectionDetails.getPassword());
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSsl(CassandraProperties properties, CqlSessionBuilder builder) {
|
||||
if (properties.isSsl()) {
|
||||
private void configureSsl(CqlSessionBuilder builder) {
|
||||
if (this.connectionDetails instanceof PropertiesCassandraConnectionDetails && this.properties.isSsl()) {
|
||||
try {
|
||||
builder.withSslContext(SSLContext.getDefault());
|
||||
}
|
||||
@@ -119,20 +133,20 @@ public class CassandraAutoConfiguration {
|
||||
|
||||
@Bean(destroyMethod = "")
|
||||
@ConditionalOnMissingBean
|
||||
public DriverConfigLoader cassandraDriverConfigLoader(CassandraProperties properties,
|
||||
public DriverConfigLoader cassandraDriverConfigLoader(
|
||||
ObjectProvider<DriverConfigLoaderBuilderCustomizer> builderCustomizers) {
|
||||
ProgrammaticDriverConfigLoaderBuilder builder = new DefaultProgrammaticDriverConfigLoaderBuilder(
|
||||
() -> cassandraConfiguration(properties), DefaultDriverConfigLoader.DEFAULT_ROOT_PATH);
|
||||
() -> cassandraConfiguration(), DefaultDriverConfigLoader.DEFAULT_ROOT_PATH);
|
||||
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Config cassandraConfiguration(CassandraProperties properties) {
|
||||
private Config cassandraConfiguration() {
|
||||
ConfigFactory.invalidateCaches();
|
||||
Config config = ConfigFactory.defaultOverrides();
|
||||
config = config.withFallback(mapConfig(properties));
|
||||
if (properties.getConfig() != null) {
|
||||
config = config.withFallback(loadConfig(properties.getConfig()));
|
||||
config = config.withFallback(mapConfig());
|
||||
if (this.properties.getConfig() != null) {
|
||||
config = config.withFallback(loadConfig(this.properties.getConfig()));
|
||||
}
|
||||
config = config.withFallback(SPRING_BOOT_DEFAULTS);
|
||||
config = config.withFallback(ConfigFactory.defaultReference());
|
||||
@@ -148,32 +162,32 @@ public class CassandraAutoConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private Config mapConfig(CassandraProperties properties) {
|
||||
private Config mapConfig() {
|
||||
CassandraDriverOptions options = new CassandraDriverOptions();
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(properties.getSessionName())
|
||||
map.from(this.properties.getSessionName())
|
||||
.whenHasText()
|
||||
.to((sessionName) -> options.add(DefaultDriverOption.SESSION_NAME, sessionName));
|
||||
map.from(properties::getUsername)
|
||||
.to((username) -> options.add(DefaultDriverOption.AUTH_PROVIDER_USER_NAME, username)
|
||||
.add(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, properties.getPassword()));
|
||||
map.from(properties::getCompression)
|
||||
map.from(this.connectionDetails.getUsername())
|
||||
.to((value) -> options.add(DefaultDriverOption.AUTH_PROVIDER_USER_NAME, value)
|
||||
.add(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, this.connectionDetails.getPassword()));
|
||||
map.from(this.properties::getCompression)
|
||||
.to((compression) -> options.add(DefaultDriverOption.PROTOCOL_COMPRESSION, compression));
|
||||
mapConnectionOptions(properties, options);
|
||||
mapPoolingOptions(properties, options);
|
||||
mapRequestOptions(properties, options);
|
||||
mapControlConnectionOptions(properties, options);
|
||||
map.from(mapContactPoints(properties))
|
||||
mapConnectionOptions(options);
|
||||
mapPoolingOptions(options);
|
||||
mapRequestOptions(options);
|
||||
mapControlConnectionOptions(options);
|
||||
map.from(mapContactPoints())
|
||||
.to((contactPoints) -> options.add(DefaultDriverOption.CONTACT_POINTS, contactPoints));
|
||||
map.from(properties.getLocalDatacenter())
|
||||
map.from(this.connectionDetails.getLocalDatacenter())
|
||||
.whenHasText()
|
||||
.to((localDatacenter) -> options.add(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER, localDatacenter));
|
||||
return options.build();
|
||||
}
|
||||
|
||||
private void mapConnectionOptions(CassandraProperties properties, CassandraDriverOptions options) {
|
||||
private void mapConnectionOptions(CassandraDriverOptions options) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
Connection connectionProperties = properties.getConnection();
|
||||
Connection connectionProperties = this.properties.getConnection();
|
||||
map.from(connectionProperties::getConnectTimeout)
|
||||
.asInt(Duration::toMillis)
|
||||
.to((connectTimeout) -> options.add(DefaultDriverOption.CONNECTION_CONNECT_TIMEOUT, connectTimeout));
|
||||
@@ -182,9 +196,9 @@ public class CassandraAutoConfiguration {
|
||||
.to((initQueryTimeout) -> options.add(DefaultDriverOption.CONNECTION_INIT_QUERY_TIMEOUT, initQueryTimeout));
|
||||
}
|
||||
|
||||
private void mapPoolingOptions(CassandraProperties properties, CassandraDriverOptions options) {
|
||||
private void mapPoolingOptions(CassandraDriverOptions options) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
CassandraProperties.Pool poolProperties = properties.getPool();
|
||||
CassandraProperties.Pool poolProperties = this.properties.getPool();
|
||||
map.from(poolProperties::getIdleTimeout)
|
||||
.asInt(Duration::toMillis)
|
||||
.to((idleTimeout) -> options.add(DefaultDriverOption.HEARTBEAT_TIMEOUT, idleTimeout));
|
||||
@@ -193,9 +207,9 @@ public class CassandraAutoConfiguration {
|
||||
.to((heartBeatInterval) -> options.add(DefaultDriverOption.HEARTBEAT_INTERVAL, heartBeatInterval));
|
||||
}
|
||||
|
||||
private void mapRequestOptions(CassandraProperties properties, CassandraDriverOptions options) {
|
||||
private void mapRequestOptions(CassandraDriverOptions options) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
Request requestProperties = properties.getRequest();
|
||||
Request requestProperties = this.properties.getRequest();
|
||||
map.from(requestProperties::getTimeout)
|
||||
.asInt(Duration::toMillis)
|
||||
.to(((timeout) -> options.add(DefaultDriverOption.REQUEST_TIMEOUT, timeout)));
|
||||
@@ -222,40 +236,19 @@ public class CassandraAutoConfiguration {
|
||||
.to((drainInterval) -> options.add(DefaultDriverOption.REQUEST_THROTTLER_DRAIN_INTERVAL, drainInterval));
|
||||
}
|
||||
|
||||
private void mapControlConnectionOptions(CassandraProperties properties, CassandraDriverOptions options) {
|
||||
private void mapControlConnectionOptions(CassandraDriverOptions options) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
Controlconnection controlProperties = properties.getControlconnection();
|
||||
Controlconnection controlProperties = this.properties.getControlconnection();
|
||||
map.from(controlProperties::getTimeout)
|
||||
.asInt(Duration::toMillis)
|
||||
.to((timeout) -> options.add(DefaultDriverOption.CONTROL_CONNECTION_TIMEOUT, timeout));
|
||||
}
|
||||
|
||||
private List<String> mapContactPoints(CassandraProperties properties) {
|
||||
if (properties.getContactPoints() != null) {
|
||||
return properties.getContactPoints()
|
||||
.stream()
|
||||
.map((candidate) -> formatContactPoint(candidate, properties.getPort()))
|
||||
.toList();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String formatContactPoint(String candidate, int port) {
|
||||
int i = candidate.lastIndexOf(':');
|
||||
if (i == -1 || !isPort(() -> candidate.substring(i + 1))) {
|
||||
return String.format("%s:%s", candidate, port);
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private boolean isPort(Supplier<String> value) {
|
||||
try {
|
||||
int i = Integer.parseInt(value.get());
|
||||
return i > 0 && i < 65535;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
private List<String> mapContactPoints() {
|
||||
return this.connectionDetails.getContactPoints()
|
||||
.stream()
|
||||
.map((node) -> node.host() + ":" + node.port())
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static class CassandraDriverOptions {
|
||||
@@ -293,4 +286,61 @@ public class CassandraAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link CassandraProperties} to {@link CassandraConnectionDetails}.
|
||||
*/
|
||||
private static final class PropertiesCassandraConnectionDetails implements CassandraConnectionDetails {
|
||||
|
||||
private final CassandraProperties properties;
|
||||
|
||||
private PropertiesCassandraConnectionDetails(CassandraProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getContactPoints() {
|
||||
List<String> contactPoints = this.properties.getContactPoints();
|
||||
return (contactPoints != null) ? contactPoints.stream().map(this::asNode).toList()
|
||||
: Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.properties.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.properties.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocalDatacenter() {
|
||||
return this.properties.getLocalDatacenter();
|
||||
}
|
||||
|
||||
private Node asNode(String contactPoint) {
|
||||
int i = contactPoint.lastIndexOf(':');
|
||||
if (i >= 0) {
|
||||
String portCandidate = contactPoint.substring(i + 1);
|
||||
Integer port = asPort(portCandidate);
|
||||
if (port != null) {
|
||||
return new Node(contactPoint.substring(0, i), port);
|
||||
}
|
||||
}
|
||||
return new Node(contactPoint, this.properties.getPort());
|
||||
}
|
||||
|
||||
private Integer asPort(String value) {
|
||||
try {
|
||||
int i = Integer.parseInt(value);
|
||||
return (i > 0 && i < 65535) ? i : null;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.cassandra;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to a Cassandra service.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface CassandraConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* Cluster node addresses.
|
||||
* @return the cluster node addresses
|
||||
*/
|
||||
List<Node> getContactPoints();
|
||||
|
||||
/**
|
||||
* Login user of the server.
|
||||
* @return the login user of the server or {@code null}
|
||||
*/
|
||||
default String getUsername() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login password of the server.
|
||||
* @return the login password of the server or {@code null}
|
||||
*/
|
||||
default String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Datacenter that is considered "local". Contact points should be from this
|
||||
* datacenter.
|
||||
* @return the datacenter that is considered "local"
|
||||
*/
|
||||
String getLocalDatacenter();
|
||||
|
||||
/**
|
||||
* A Cassandra node.
|
||||
*
|
||||
* @param host the hostname
|
||||
* @param port the port
|
||||
*/
|
||||
record Node(String host, int port) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,9 +18,12 @@ package org.springframework.boot.autoconfigure.data.mongo;
|
||||
|
||||
import com.mongodb.client.MongoClient;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
|
||||
import org.springframework.boot.autoconfigure.mongo.PropertiesMongoConnectionDetails;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.mongodb.MongoDatabaseFactory;
|
||||
@@ -32,6 +35,8 @@ import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(MongoDatabaseFactory.class)
|
||||
@@ -39,8 +44,12 @@ import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
|
||||
class MongoDatabaseFactoryConfiguration {
|
||||
|
||||
@Bean
|
||||
MongoDatabaseFactorySupport<?> mongoDatabaseFactory(MongoClient mongoClient, MongoProperties properties) {
|
||||
return new SimpleMongoClientDatabaseFactory(mongoClient, properties.getMongoClientDatabase());
|
||||
MongoDatabaseFactorySupport<?> mongoDatabaseFactory(MongoClient mongoClient, MongoProperties properties,
|
||||
ObjectProvider<MongoConnectionDetails> connectionDetails) {
|
||||
return new SimpleMongoClientDatabaseFactory(mongoClient,
|
||||
connectionDetails.getIfAvailable(() -> new PropertiesMongoConnectionDetails(properties))
|
||||
.getConnectionString()
|
||||
.getDatabase());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* 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.
|
||||
@@ -20,10 +20,14 @@ import com.mongodb.ClientSessionOptions;
|
||||
import com.mongodb.client.ClientSession;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails.GridFs;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoProperties.Gridfs;
|
||||
import org.springframework.boot.autoconfigure.mongo.PropertiesMongoConnectionDetails;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
@@ -46,17 +50,13 @@ import org.springframework.util.StringUtils;
|
||||
* Configuration for Mongo-related beans that depend on a {@link MongoDatabaseFactory}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Moritz Halbritter
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(MongoDatabaseFactory.class)
|
||||
class MongoDatabaseFactoryDependentConfiguration {
|
||||
|
||||
private final MongoProperties properties;
|
||||
|
||||
MongoDatabaseFactoryDependentConfiguration(MongoProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MongoOperations.class)
|
||||
MongoTemplate mongoTemplate(MongoDatabaseFactory factory, MongoConverter converter) {
|
||||
@@ -75,31 +75,36 @@ class MongoDatabaseFactoryDependentConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(GridFsOperations.class)
|
||||
GridFsTemplate gridFsTemplate(MongoDatabaseFactory factory, MongoTemplate mongoTemplate) {
|
||||
return new GridFsTemplate(new GridFsMongoDatabaseFactory(factory, this.properties),
|
||||
mongoTemplate.getConverter(), this.properties.getGridfs().getBucket());
|
||||
GridFsTemplate gridFsTemplate(MongoProperties properties, MongoDatabaseFactory factory, MongoTemplate mongoTemplate,
|
||||
ObjectProvider<MongoConnectionDetails> connectionDetailsProvider) {
|
||||
MongoConnectionDetails connectionDetails = connectionDetailsProvider
|
||||
.getIfAvailable(() -> new PropertiesMongoConnectionDetails(properties));
|
||||
return new GridFsTemplate(new GridFsMongoDatabaseFactory(factory, connectionDetails),
|
||||
mongoTemplate.getConverter(),
|
||||
(connectionDetails.getGridFs() != null) ? connectionDetails.getGridFs().getBucket() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link MongoDatabaseFactory} decorator to respect {@link Gridfs#getDatabase()} if
|
||||
* set.
|
||||
* {@link MongoDatabaseFactory} decorator to respect {@link Gridfs#getDatabase()} or
|
||||
* {@link GridFs#getGridFs()} from the {@link MongoConnectionDetails} if set.
|
||||
*/
|
||||
static class GridFsMongoDatabaseFactory implements MongoDatabaseFactory {
|
||||
|
||||
private final MongoDatabaseFactory mongoDatabaseFactory;
|
||||
|
||||
private final MongoProperties properties;
|
||||
private final MongoConnectionDetails connectionDetails;
|
||||
|
||||
GridFsMongoDatabaseFactory(MongoDatabaseFactory mongoDatabaseFactory, MongoProperties properties) {
|
||||
GridFsMongoDatabaseFactory(MongoDatabaseFactory mongoDatabaseFactory,
|
||||
MongoConnectionDetails connectionDetails) {
|
||||
Assert.notNull(mongoDatabaseFactory, "MongoDatabaseFactory must not be null");
|
||||
Assert.notNull(properties, "Properties must not be null");
|
||||
Assert.notNull(connectionDetails, "ConnectionDetails must not be null");
|
||||
this.mongoDatabaseFactory = mongoDatabaseFactory;
|
||||
this.properties = properties;
|
||||
this.connectionDetails = connectionDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoDatabase getMongoDatabase() throws DataAccessException {
|
||||
String gridFsDatabase = this.properties.getGridfs().getDatabase();
|
||||
String gridFsDatabase = getGridFsDatabase(this.connectionDetails);
|
||||
if (StringUtils.hasText(gridFsDatabase)) {
|
||||
return this.mongoDatabaseFactory.getMongoDatabase(gridFsDatabase);
|
||||
}
|
||||
@@ -126,6 +131,10 @@ class MongoDatabaseFactoryDependentConfiguration {
|
||||
return this.mongoDatabaseFactory.withSession(session);
|
||||
}
|
||||
|
||||
private String getGridFsDatabase(MongoConnectionDetails connectionDetails) {
|
||||
return (connectionDetails.getGridFs() != null) ? connectionDetails.getGridFs().getDatabase() : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -26,14 +26,17 @@ import org.bson.codecs.Codec;
|
||||
import org.bson.codecs.configuration.CodecRegistry;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
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.mongo.MongoConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails.GridFs;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoProperties.Gridfs;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mongo.PropertiesMongoConnectionDetails;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -60,12 +63,12 @@ import org.springframework.util.StringUtils;
|
||||
* <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
|
||||
* @author Artsiom Yudovin
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@AutoConfiguration(after = MongoReactiveAutoConfiguration.class)
|
||||
@@ -75,12 +78,19 @@ import org.springframework.util.StringUtils;
|
||||
@Import(MongoDataConfiguration.class)
|
||||
public class MongoReactiveDataAutoConfiguration {
|
||||
|
||||
private final MongoConnectionDetails connectionDetails;
|
||||
|
||||
MongoReactiveDataAutoConfiguration(MongoProperties properties,
|
||||
ObjectProvider<MongoConnectionDetails> connectionDetails) {
|
||||
this.connectionDetails = connectionDetails
|
||||
.getIfAvailable(() -> new PropertiesMongoConnectionDetails(properties));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ReactiveMongoDatabaseFactory.class)
|
||||
public SimpleReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory(MongoProperties properties,
|
||||
MongoClient mongo) {
|
||||
String database = properties.getMongoClientDatabase();
|
||||
return new SimpleReactiveMongoDatabaseFactory(mongo, database);
|
||||
public SimpleReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory(MongoClient mongo) {
|
||||
return new SimpleReactiveMongoDatabaseFactory(mongo,
|
||||
this.connectionDetails.getConnectionString().getDatabase());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -108,26 +118,27 @@ public class MongoReactiveDataAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ReactiveGridFsOperations.class)
|
||||
public ReactiveGridFsTemplate reactiveGridFsTemplate(ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory,
|
||||
MappingMongoConverter mappingMongoConverter, DataBufferFactory dataBufferFactory,
|
||||
MongoProperties properties) {
|
||||
MappingMongoConverter mappingMongoConverter, DataBufferFactory dataBufferFactory) {
|
||||
return new ReactiveGridFsTemplate(dataBufferFactory,
|
||||
new GridFsReactiveMongoDatabaseFactory(reactiveMongoDatabaseFactory, properties), mappingMongoConverter,
|
||||
properties.getGridfs().getBucket());
|
||||
new GridFsReactiveMongoDatabaseFactory(reactiveMongoDatabaseFactory, this.connectionDetails),
|
||||
mappingMongoConverter,
|
||||
(this.connectionDetails.getGridFs() != null) ? this.connectionDetails.getGridFs().getBucket() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ReactiveMongoDatabaseFactory} decorator to use {@link Gridfs#getDatabase()}
|
||||
* when set.
|
||||
* {@link ReactiveMongoDatabaseFactory} decorator to use {@link GridFs#getGridFs()}
|
||||
* from the {@link MongoConnectionDetails} when set.
|
||||
*/
|
||||
static class GridFsReactiveMongoDatabaseFactory implements ReactiveMongoDatabaseFactory {
|
||||
|
||||
private final ReactiveMongoDatabaseFactory delegate;
|
||||
|
||||
private final MongoProperties properties;
|
||||
private final MongoConnectionDetails connectionDetails;
|
||||
|
||||
GridFsReactiveMongoDatabaseFactory(ReactiveMongoDatabaseFactory delegate, MongoProperties properties) {
|
||||
GridFsReactiveMongoDatabaseFactory(ReactiveMongoDatabaseFactory delegate,
|
||||
MongoConnectionDetails connectionDetails) {
|
||||
this.delegate = delegate;
|
||||
this.properties = properties;
|
||||
this.connectionDetails = connectionDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -137,13 +148,17 @@ public class MongoReactiveDataAutoConfiguration {
|
||||
|
||||
@Override
|
||||
public Mono<MongoDatabase> getMongoDatabase() throws DataAccessException {
|
||||
String gridFsDatabase = this.properties.getGridfs().getDatabase();
|
||||
String gridFsDatabase = getGridFsDatabase(this.connectionDetails);
|
||||
if (StringUtils.hasText(gridFsDatabase)) {
|
||||
return this.delegate.getMongoDatabase(gridFsDatabase);
|
||||
}
|
||||
return this.delegate.getMongoDatabase();
|
||||
}
|
||||
|
||||
private String getGridFsDatabase(MongoConnectionDetails connectionDetails) {
|
||||
return (connectionDetails.getGridFs() != null) ? connectionDetails.getGridFs().getDatabase() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<MongoDatabase> getMongoDatabase(String dbName) throws DataAccessException {
|
||||
return this.delegate.getMongoDatabase(dbName);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -42,6 +42,9 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
|
||||
@@ -52,8 +55,10 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
JedisConnectionConfiguration(RedisProperties properties,
|
||||
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
|
||||
ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfiguration) {
|
||||
super(properties, standaloneConfigurationProvider, sentinelConfiguration, clusterConfiguration);
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfiguration,
|
||||
ObjectProvider<RedisConnectionDetails> connectionDetailsProvider) {
|
||||
super(properties, standaloneConfigurationProvider, sentinelConfiguration, clusterConfiguration,
|
||||
connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -90,7 +95,9 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
|
||||
private JedisClientConfigurationBuilder applyProperties(JedisClientConfigurationBuilder builder) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(getProperties().isSsl()).whenTrue().toCall(builder::useSsl);
|
||||
boolean ssl = (!(getConnectionDetails() instanceof PropertiesRedisConnectionDetails)) ? false
|
||||
: getProperties().isSsl();
|
||||
map.from(ssl).whenTrue().toCall(builder::useSsl);
|
||||
map.from(getProperties().getTimeout()).to(builder::readTimeout);
|
||||
map.from(getProperties().getConnectTimeout()).to(builder::connectTimeout);
|
||||
map.from(getProperties().getClientName()).whenHasText().to(builder::clientName);
|
||||
@@ -117,8 +124,7 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
}
|
||||
|
||||
private void customizeConfigurationFromUrl(JedisClientConfiguration.JedisClientConfigurationBuilder builder) {
|
||||
ConnectionInfo connectionInfo = parseUrl(getProperties().getUrl());
|
||||
if (connectionInfo.isUseSsl()) {
|
||||
if (urlUsesSsl()) {
|
||||
builder.useSsl();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Andy Wilkinson
|
||||
* @author Moritz Halbritter
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(RedisClient.class)
|
||||
@@ -61,8 +63,10 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
LettuceConnectionConfiguration(RedisProperties properties,
|
||||
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
|
||||
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
|
||||
super(properties, standaloneConfigurationProvider, sentinelConfigurationProvider, clusterConfigurationProvider);
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,
|
||||
ObjectProvider<RedisConnectionDetails> connectionDetailsProvider) {
|
||||
super(properties, standaloneConfigurationProvider, sentinelConfigurationProvider, clusterConfigurationProvider,
|
||||
connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Bean(destroyMethod = "shutdown")
|
||||
@@ -116,7 +120,7 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
|
||||
private LettuceClientConfigurationBuilder applyProperties(
|
||||
LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {
|
||||
if (getProperties().isSsl()) {
|
||||
if (getConnectionDetails() instanceof PropertiesRedisConnectionDetails && getProperties().isSsl()) {
|
||||
builder.useSsl();
|
||||
}
|
||||
if (getProperties().getTimeout() != null) {
|
||||
@@ -161,8 +165,7 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
}
|
||||
|
||||
private void customizeConfigurationFromUrl(LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {
|
||||
ConnectionInfo connectionInfo = parseUrl(getProperties().getUrl());
|
||||
if (connectionInfo.isUseSsl()) {
|
||||
if (urlUsesSsl()) {
|
||||
builder.useSsl();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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.data.redis;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionConfiguration.ConnectionInfo;
|
||||
|
||||
/**
|
||||
* Adapts {@link RedisProperties} to {@link RedisConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class PropertiesRedisConnectionDetails implements RedisConnectionDetails {
|
||||
|
||||
private final RedisProperties properties;
|
||||
|
||||
PropertiesRedisConnectionDetails(RedisProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
if (this.properties.getUrl() != null) {
|
||||
ConnectionInfo connectionInfo = connectionInfo(this.properties.getUrl());
|
||||
String userInfo = connectionInfo.getUri().getUserInfo();
|
||||
int index = (userInfo != null) ? userInfo.indexOf(':') : -1;
|
||||
if (index != -1) {
|
||||
return userInfo.substring(0, index);
|
||||
}
|
||||
}
|
||||
return this.properties.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
if (this.properties.getUrl() != null) {
|
||||
ConnectionInfo connectionInfo = connectionInfo(this.properties.getUrl());
|
||||
String userInfo = connectionInfo.getUri().getUserInfo();
|
||||
int index = (userInfo != null) ? userInfo.indexOf(':') : -1;
|
||||
if (index != -1) {
|
||||
return userInfo.substring(index + 1);
|
||||
}
|
||||
}
|
||||
return this.properties.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Standalone getStandalone() {
|
||||
if (this.properties.getUrl() != null) {
|
||||
ConnectionInfo connectionInfo = connectionInfo(this.properties.getUrl());
|
||||
return Standalone.of(connectionInfo.getUri().getHost(), connectionInfo.getUri().getPort(),
|
||||
this.properties.getDatabase());
|
||||
}
|
||||
return Standalone.of(this.properties.getHost(), this.properties.getPort(), this.properties.getDatabase());
|
||||
}
|
||||
|
||||
private ConnectionInfo connectionInfo(String url) {
|
||||
return (url != null) ? RedisConnectionConfiguration.parseUrl(url) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sentinel getSentinel() {
|
||||
org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel sentinel = this.properties
|
||||
.getSentinel();
|
||||
if (sentinel == null) {
|
||||
return null;
|
||||
}
|
||||
return new Sentinel() {
|
||||
|
||||
@Override
|
||||
public int getDatabase() {
|
||||
return PropertiesRedisConnectionDetails.this.properties.getDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMaster() {
|
||||
return sentinel.getMaster();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getNodes() {
|
||||
return sentinel.getNodes().stream().map(PropertiesRedisConnectionDetails.this::asNode).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return sentinel.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return sentinel.getPassword();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cluster getCluster() {
|
||||
RedisProperties.Cluster cluster = this.properties.getCluster();
|
||||
List<Node> nodes = (cluster != null) ? cluster.getNodes().stream().map(this::asNode).toList() : null;
|
||||
return (nodes != null) ? () -> nodes : null;
|
||||
}
|
||||
|
||||
private Node asNode(String node) {
|
||||
String[] components = node.split(":");
|
||||
return new Node(components[0], Integer.parseInt(components[1]));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -22,6 +22,9 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails.Cluster;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails.Node;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails.Sentinel;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Pool;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisNode;
|
||||
@@ -29,7 +32,6 @@ import org.springframework.data.redis.connection.RedisPassword;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base Redis connection configuration.
|
||||
@@ -39,6 +41,9 @@ import org.springframework.util.StringUtils;
|
||||
* @author Alen Turkovic
|
||||
* @author Scott Frederick
|
||||
* @author Eddú Meléndez
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class RedisConnectionConfiguration {
|
||||
|
||||
@@ -53,14 +58,19 @@ abstract class RedisConnectionConfiguration {
|
||||
|
||||
private final RedisClusterConfiguration clusterConfiguration;
|
||||
|
||||
private final RedisConnectionDetails connectionDetails;
|
||||
|
||||
protected RedisConnectionConfiguration(RedisProperties properties,
|
||||
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
|
||||
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,
|
||||
ObjectProvider<RedisConnectionDetails> connectionDetailsProvider) {
|
||||
this.properties = properties;
|
||||
this.standaloneConfiguration = standaloneConfigurationProvider.getIfAvailable();
|
||||
this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable();
|
||||
this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable();
|
||||
this.connectionDetails = connectionDetailsProvider
|
||||
.getIfAvailable(() -> new PropertiesRedisConnectionDetails(properties));
|
||||
}
|
||||
|
||||
protected final RedisStandaloneConfiguration getStandaloneConfig() {
|
||||
@@ -68,20 +78,11 @@ abstract class RedisConnectionConfiguration {
|
||||
return this.standaloneConfiguration;
|
||||
}
|
||||
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
|
||||
if (StringUtils.hasText(this.properties.getUrl())) {
|
||||
ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl());
|
||||
config.setHostName(connectionInfo.getHostName());
|
||||
config.setPort(connectionInfo.getPort());
|
||||
config.setUsername(connectionInfo.getUsername());
|
||||
config.setPassword(RedisPassword.of(connectionInfo.getPassword()));
|
||||
}
|
||||
else {
|
||||
config.setHostName(this.properties.getHost());
|
||||
config.setPort(this.properties.getPort());
|
||||
config.setUsername(this.properties.getUsername());
|
||||
config.setPassword(RedisPassword.of(this.properties.getPassword()));
|
||||
}
|
||||
config.setDatabase(this.properties.getDatabase());
|
||||
config.setHostName(this.connectionDetails.getStandalone().getHost());
|
||||
config.setPort(this.connectionDetails.getStandalone().getPort());
|
||||
config.setUsername(this.connectionDetails.getUsername());
|
||||
config.setPassword(RedisPassword.of(this.connectionDetails.getPassword()));
|
||||
config.setDatabase(this.connectionDetails.getStandalone().getDatabase());
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -89,20 +90,21 @@ abstract class RedisConnectionConfiguration {
|
||||
if (this.sentinelConfiguration != null) {
|
||||
return this.sentinelConfiguration;
|
||||
}
|
||||
RedisProperties.Sentinel sentinelProperties = this.properties.getSentinel();
|
||||
if (sentinelProperties != null) {
|
||||
if (this.connectionDetails.getSentinel() != null) {
|
||||
RedisSentinelConfiguration config = new RedisSentinelConfiguration();
|
||||
config.master(sentinelProperties.getMaster());
|
||||
config.setSentinels(createSentinels(sentinelProperties));
|
||||
config.setUsername(this.properties.getUsername());
|
||||
if (this.properties.getPassword() != null) {
|
||||
config.setPassword(RedisPassword.of(this.properties.getPassword()));
|
||||
config.master(this.connectionDetails.getSentinel().getMaster());
|
||||
config.setSentinels(createSentinels(this.connectionDetails.getSentinel()));
|
||||
config.setUsername(this.connectionDetails.getUsername());
|
||||
String password = this.connectionDetails.getPassword();
|
||||
if (password != null) {
|
||||
config.setPassword(RedisPassword.of(password));
|
||||
}
|
||||
config.setSentinelUsername(sentinelProperties.getUsername());
|
||||
if (sentinelProperties.getPassword() != null) {
|
||||
config.setSentinelPassword(RedisPassword.of(sentinelProperties.getPassword()));
|
||||
config.setSentinelUsername(this.connectionDetails.getSentinel().getUsername());
|
||||
String sentinelPassword = this.connectionDetails.getSentinel().getPassword();
|
||||
if (sentinelPassword != null) {
|
||||
config.setSentinelPassword(RedisPassword.of(sentinelPassword));
|
||||
}
|
||||
config.setDatabase(this.properties.getDatabase());
|
||||
config.setDatabase(this.connectionDetails.getSentinel().getDatabase());
|
||||
return config;
|
||||
}
|
||||
return null;
|
||||
@@ -116,19 +118,25 @@ abstract class RedisConnectionConfiguration {
|
||||
if (this.clusterConfiguration != null) {
|
||||
return this.clusterConfiguration;
|
||||
}
|
||||
if (this.properties.getCluster() == null) {
|
||||
return null;
|
||||
}
|
||||
RedisProperties.Cluster clusterProperties = this.properties.getCluster();
|
||||
RedisClusterConfiguration config = new RedisClusterConfiguration(clusterProperties.getNodes());
|
||||
if (clusterProperties.getMaxRedirects() != null) {
|
||||
config.setMaxRedirects(clusterProperties.getMaxRedirects());
|
||||
if (this.connectionDetails.getCluster() != null) {
|
||||
RedisClusterConfiguration config = new RedisClusterConfiguration(
|
||||
getNodes(this.connectionDetails.getCluster()));
|
||||
if (clusterProperties != null && clusterProperties.getMaxRedirects() != null) {
|
||||
config.setMaxRedirects(clusterProperties.getMaxRedirects());
|
||||
}
|
||||
config.setUsername(this.connectionDetails.getUsername());
|
||||
String password = this.connectionDetails.getPassword();
|
||||
if (password != null) {
|
||||
config.setPassword(RedisPassword.of(password));
|
||||
}
|
||||
return config;
|
||||
}
|
||||
config.setUsername(this.properties.getUsername());
|
||||
if (this.properties.getPassword() != null) {
|
||||
config.setPassword(RedisPassword.of(this.properties.getPassword()));
|
||||
}
|
||||
return config;
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<String> getNodes(Cluster cluster) {
|
||||
return cluster.getNodes().stream().map((node) -> "%s:%d".formatted(node.host(), node.port())).toList();
|
||||
}
|
||||
|
||||
protected final RedisProperties getProperties() {
|
||||
@@ -140,20 +148,23 @@ abstract class RedisConnectionConfiguration {
|
||||
return (enabled != null) ? enabled : COMMONS_POOL2_AVAILABLE;
|
||||
}
|
||||
|
||||
private List<RedisNode> createSentinels(RedisProperties.Sentinel sentinel) {
|
||||
private List<RedisNode> createSentinels(Sentinel sentinel) {
|
||||
List<RedisNode> nodes = new ArrayList<>();
|
||||
for (String node : sentinel.getNodes()) {
|
||||
try {
|
||||
nodes.add(RedisNode.fromString(node));
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw new IllegalStateException("Invalid redis sentinel property '" + node + "'", ex);
|
||||
}
|
||||
for (Node node : sentinel.getNodes()) {
|
||||
nodes.add(new RedisNode(node.host(), node.port()));
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
protected ConnectionInfo parseUrl(String url) {
|
||||
protected final boolean urlUsesSsl() {
|
||||
return parseUrl(this.properties.getUrl()).isUseSsl();
|
||||
}
|
||||
|
||||
protected final RedisConnectionDetails getConnectionDetails() {
|
||||
return this.connectionDetails;
|
||||
}
|
||||
|
||||
static ConnectionInfo parseUrl(String url) {
|
||||
try {
|
||||
URI uri = new URI(url);
|
||||
String scheme = uri.getScheme();
|
||||
@@ -198,18 +209,14 @@ abstract class RedisConnectionConfiguration {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
URI getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
boolean isUseSsl() {
|
||||
return this.useSsl;
|
||||
}
|
||||
|
||||
String getHostName() {
|
||||
return this.uri.getHost();
|
||||
}
|
||||
|
||||
int getPort() {
|
||||
return this.uri.getPort();
|
||||
}
|
||||
|
||||
String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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.data.redis;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to a Redis service.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface RedisConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* Login username of the redis server.
|
||||
* @return the login username of the redis server
|
||||
*/
|
||||
default String getUsername() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login password of the redis server.
|
||||
* @return the login password of the redis server
|
||||
*/
|
||||
default String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis standalone configuration. Mutually exclusive with {@link #getSentinel()} and
|
||||
* {@link #getCluster()}.
|
||||
* @return the Redis standalone configuration
|
||||
*/
|
||||
default Standalone getStandalone() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis sentinel configuration. Mutually exclusive with {@link #getStandalone()} and
|
||||
* {@link #getCluster()}.
|
||||
* @return the Redis sentinel configuration
|
||||
*/
|
||||
default Sentinel getSentinel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis cluster configuration. Mutually exclusive with {@link #getStandalone()} and
|
||||
* {@link #getSentinel()}.
|
||||
* @return the Redis cluster configuration
|
||||
*/
|
||||
default Cluster getCluster() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis standalone configuration.
|
||||
*/
|
||||
interface Standalone {
|
||||
|
||||
/**
|
||||
* Redis server host.
|
||||
* @return the redis server host
|
||||
*/
|
||||
String getHost();
|
||||
|
||||
/**
|
||||
* Redis server port.
|
||||
* @return the redis server port
|
||||
*/
|
||||
int getPort();
|
||||
|
||||
/**
|
||||
* Database index used by the connection factory.
|
||||
* @return the database index used by the connection factory
|
||||
*/
|
||||
default int getDatabase() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Standalone of(String host, int port) {
|
||||
return of(host, port, 0);
|
||||
}
|
||||
|
||||
static Standalone of(String host, int port, int database) {
|
||||
Assert.hasLength(host, "Host must not be empty");
|
||||
return new Standalone() {
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis sentinel configuration.
|
||||
*/
|
||||
interface Sentinel {
|
||||
|
||||
/**
|
||||
* Database index used by the connection factory.
|
||||
* @return the database index used by the connection factory
|
||||
*/
|
||||
int getDatabase();
|
||||
|
||||
/**
|
||||
* Name of the Redis server.
|
||||
* @return the name of the Redis server
|
||||
*/
|
||||
String getMaster();
|
||||
|
||||
/**
|
||||
* List of nodes.
|
||||
* @return the list of nodes
|
||||
*/
|
||||
List<Node> getNodes();
|
||||
|
||||
/**
|
||||
* Login username for authenticating with sentinel(s).
|
||||
* @return the login username for authenticating with sentinel(s) or {@code null}
|
||||
*/
|
||||
String getUsername();
|
||||
|
||||
/**
|
||||
* Password for authenticating with sentinel(s).
|
||||
* @return the password for authenticating with sentinel(s) or {@code null}
|
||||
*/
|
||||
String getPassword();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis cluster configuration.
|
||||
*/
|
||||
interface Cluster {
|
||||
|
||||
/**
|
||||
* Nodes to bootstrap from. This represents an "initial" list of cluster nodes and
|
||||
* is required to have at least one entry.
|
||||
* @return nodes to bootstrap from
|
||||
*/
|
||||
List<Node> getNodes();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A node in a sentinel or cluster configuration.
|
||||
*
|
||||
* @param host the hostname of the node
|
||||
* @param port the port of the node
|
||||
*/
|
||||
record Node(String host, int port) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.elasticsearch;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to an Elasticsearch service.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface ElasticsearchConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* List of the Elasticsearch nodes to use.
|
||||
* @return list of the Elasticsearch nodes to use
|
||||
*/
|
||||
List<Node> getNodes();
|
||||
|
||||
/**
|
||||
* Username for authentication with Elasticsearch.
|
||||
* @return username for authentication with Elasticsearch or {@code null}
|
||||
*/
|
||||
default String getUsername() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Password for authentication with Elasticsearch.
|
||||
* @return password for authentication with Elasticsearch or {@code null}
|
||||
*/
|
||||
default String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefix added to the path of every request sent to Elasticsearch.
|
||||
* @return prefix added to the path of every request sent to Elasticsearch or
|
||||
* {@code null}
|
||||
*/
|
||||
default String getPathPrefix() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* An Elasticsearch node.
|
||||
*
|
||||
* @param hostname the hostname
|
||||
* @param port the port
|
||||
* @param protocol the protocol
|
||||
* @param username the username or {@code null}
|
||||
* @param password the password or {@code null}
|
||||
*/
|
||||
record Node(String hostname, int port, Node.Protocol protocol, String username, String password) {
|
||||
|
||||
public Node(String host, int port, Node.Protocol protocol) {
|
||||
this(host, port, protocol, null, null);
|
||||
}
|
||||
|
||||
URI toUri() {
|
||||
try {
|
||||
return new URI(this.protocol.getScheme(), userInfo(), this.hostname, this.port, null, null, null);
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
throw new IllegalStateException("Can't construct URI", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String userInfo() {
|
||||
if (this.username == null) {
|
||||
return null;
|
||||
}
|
||||
return (this.password != null) ? (this.username + ":" + this.password) : this.username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection protocol.
|
||||
*/
|
||||
public enum Protocol {
|
||||
|
||||
/**
|
||||
* HTTP.
|
||||
*/
|
||||
HTTP("http"),
|
||||
|
||||
/**
|
||||
* HTTPS.
|
||||
*/
|
||||
HTTPS("https");
|
||||
|
||||
private final String scheme;
|
||||
|
||||
Protocol(String scheme) {
|
||||
this.scheme = scheme;
|
||||
}
|
||||
|
||||
String getScheme() {
|
||||
return this.scheme;
|
||||
}
|
||||
|
||||
static Protocol forScheme(String scheme) {
|
||||
for (Protocol protocol : values()) {
|
||||
if (protocol.scheme.equals(scheme)) {
|
||||
return protocol;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown scheme '" + scheme + "'");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,8 +17,9 @@
|
||||
package org.springframework.boot.autoconfigure.elasticsearch;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.auth.AuthScope;
|
||||
@@ -37,6 +38,8 @@ import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails.Node;
|
||||
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails.Node.Protocol;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -47,6 +50,9 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Filip Hrisafov
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ElasticsearchRestClientConfigurations {
|
||||
|
||||
@@ -56,20 +62,27 @@ class ElasticsearchRestClientConfigurations {
|
||||
|
||||
private final ElasticsearchProperties properties;
|
||||
|
||||
RestClientBuilderConfiguration(ElasticsearchProperties properties) {
|
||||
private final ElasticsearchConnectionDetails connectionDetails;
|
||||
|
||||
RestClientBuilderConfiguration(ElasticsearchProperties properties,
|
||||
ObjectProvider<ElasticsearchConnectionDetails> connectionDetails) {
|
||||
this.properties = properties;
|
||||
this.connectionDetails = connectionDetails
|
||||
.getIfAvailable(() -> new PropertiesElasticsearchConnectionDetails(properties));
|
||||
}
|
||||
|
||||
@Bean
|
||||
RestClientBuilderCustomizer defaultRestClientBuilderCustomizer() {
|
||||
return new DefaultRestClientBuilderCustomizer(this.properties);
|
||||
return new DefaultRestClientBuilderCustomizer(this.properties, this.connectionDetails);
|
||||
}
|
||||
|
||||
@Bean
|
||||
RestClientBuilder elasticsearchRestClientBuilder(
|
||||
ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) {
|
||||
HttpHost[] hosts = this.properties.getUris().stream().map(this::createHttpHost).toArray(HttpHost[]::new);
|
||||
RestClientBuilder builder = RestClient.builder(hosts);
|
||||
RestClientBuilder builder = RestClient.builder(this.connectionDetails.getNodes()
|
||||
.stream()
|
||||
.map((node) -> new HttpHost(node.hostname(), node.port(), node.protocol().getScheme()))
|
||||
.toArray(HttpHost[]::new));
|
||||
builder.setHttpClientConfigCallback((httpClientBuilder) -> {
|
||||
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(httpClientBuilder));
|
||||
return httpClientBuilder;
|
||||
@@ -78,36 +91,14 @@ class ElasticsearchRestClientConfigurations {
|
||||
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(requestConfigBuilder));
|
||||
return requestConfigBuilder;
|
||||
});
|
||||
if (this.properties.getPathPrefix() != null) {
|
||||
builder.setPathPrefix(this.properties.getPathPrefix());
|
||||
String pathPrefix = this.connectionDetails.getPathPrefix();
|
||||
if (pathPrefix != null) {
|
||||
builder.setPathPrefix(pathPrefix);
|
||||
}
|
||||
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder;
|
||||
}
|
||||
|
||||
private HttpHost createHttpHost(String uri) {
|
||||
try {
|
||||
return createHttpHost(URI.create(uri));
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
return HttpHost.create(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private HttpHost createHttpHost(URI uri) {
|
||||
if (!StringUtils.hasLength(uri.getUserInfo())) {
|
||||
return HttpHost.create(uri.toString());
|
||||
}
|
||||
try {
|
||||
return HttpHost.create(new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(),
|
||||
uri.getQuery(), uri.getFragment())
|
||||
.toString());
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@@ -146,8 +137,12 @@ class ElasticsearchRestClientConfigurations {
|
||||
|
||||
private final ElasticsearchProperties properties;
|
||||
|
||||
DefaultRestClientBuilderCustomizer(ElasticsearchProperties properties) {
|
||||
private final ElasticsearchConnectionDetails connectionDetails;
|
||||
|
||||
DefaultRestClientBuilderCustomizer(ElasticsearchProperties properties,
|
||||
ElasticsearchConnectionDetails connectionDetails) {
|
||||
this.properties = properties;
|
||||
this.connectionDetails = connectionDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,7 +151,7 @@ class ElasticsearchRestClientConfigurations {
|
||||
|
||||
@Override
|
||||
public void customize(HttpAsyncClientBuilder builder) {
|
||||
builder.setDefaultCredentialsProvider(new PropertiesCredentialsProvider(this.properties));
|
||||
builder.setDefaultCredentialsProvider(new ConnectionDetailsCredentialsProvider(this.connectionDetails));
|
||||
map.from(this.properties::isSocketKeepAlive)
|
||||
.to((keepAlive) -> builder
|
||||
.setDefaultIOReactorConfig(IOReactorConfig.custom().setSoKeepAlive(keepAlive).build()));
|
||||
@@ -176,28 +171,20 @@ class ElasticsearchRestClientConfigurations {
|
||||
|
||||
}
|
||||
|
||||
private static class PropertiesCredentialsProvider extends BasicCredentialsProvider {
|
||||
private static class ConnectionDetailsCredentialsProvider extends BasicCredentialsProvider {
|
||||
|
||||
PropertiesCredentialsProvider(ElasticsearchProperties properties) {
|
||||
if (StringUtils.hasText(properties.getUsername())) {
|
||||
Credentials credentials = new UsernamePasswordCredentials(properties.getUsername(),
|
||||
properties.getPassword());
|
||||
ConnectionDetailsCredentialsProvider(ElasticsearchConnectionDetails connectionDetails) {
|
||||
String username = connectionDetails.getUsername();
|
||||
if (StringUtils.hasText(username)) {
|
||||
Credentials credentials = new UsernamePasswordCredentials(username, connectionDetails.getPassword());
|
||||
setCredentials(AuthScope.ANY, credentials);
|
||||
}
|
||||
properties.getUris()
|
||||
.stream()
|
||||
.map(this::toUri)
|
||||
.filter(this::hasUserInfo)
|
||||
.forEach(this::addUserInfoCredentials);
|
||||
Stream<URI> uris = getUris(connectionDetails);
|
||||
uris.filter(this::hasUserInfo).forEach(this::addUserInfoCredentials);
|
||||
}
|
||||
|
||||
private URI toUri(String uri) {
|
||||
try {
|
||||
return URI.create(uri);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
return null;
|
||||
}
|
||||
private Stream<URI> getUris(ElasticsearchConnectionDetails connectionDetails) {
|
||||
return connectionDetails.getNodes().stream().map(Node::toUri);
|
||||
}
|
||||
|
||||
private boolean hasUserInfo(URI uri) {
|
||||
@@ -222,4 +209,59 @@ class ElasticsearchRestClientConfigurations {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link ElasticsearchProperties} to {@link ElasticsearchConnectionDetails}.
|
||||
*/
|
||||
private static class PropertiesElasticsearchConnectionDetails implements ElasticsearchConnectionDetails {
|
||||
|
||||
private final ElasticsearchProperties properties;
|
||||
|
||||
PropertiesElasticsearchConnectionDetails(ElasticsearchProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getNodes() {
|
||||
return this.properties.getUris().stream().map(this::createNode).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.properties.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.properties.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathPrefix() {
|
||||
return this.properties.getPathPrefix();
|
||||
}
|
||||
|
||||
private Node createNode(String uri) {
|
||||
if (!(uri.startsWith("http://") || uri.startsWith("https://"))) {
|
||||
uri = "http://" + uri;
|
||||
}
|
||||
return createNode(URI.create(uri));
|
||||
}
|
||||
|
||||
private Node createNode(URI uri) {
|
||||
String userInfo = uri.getUserInfo();
|
||||
Protocol protocol = Protocol.forScheme(uri.getScheme());
|
||||
if (!StringUtils.hasLength(userInfo)) {
|
||||
return new Node(uri.getHost(), uri.getPort(), protocol, null, null);
|
||||
}
|
||||
int separatorIndex = userInfo.indexOf(':');
|
||||
if (separatorIndex == -1) {
|
||||
return new Node(uri.getHost(), uri.getPort(), protocol, userInfo, null);
|
||||
}
|
||||
String[] components = userInfo.split(":");
|
||||
return new Node(uri.getHost(), uri.getPort(), protocol, components[0],
|
||||
(components.length > 1) ? components[1] : "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayAutoConfigurationRuntimeHints;
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayDataSourceCondition;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
|
||||
@@ -86,6 +87,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Semyon Danilov
|
||||
* @author Chris Bono
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@AutoConfiguration(after = { DataSourceAutoConfiguration.class, JdbcTemplateAutoConfiguration.class,
|
||||
@@ -125,7 +127,7 @@ public class FlywayAutoConfiguration {
|
||||
ObjectProvider<FlywayConfigurationCustomizer> fluentConfigurationCustomizers,
|
||||
ObjectProvider<JavaMigration> javaMigrations, ObjectProvider<Callback> callbacks) {
|
||||
return flyway(properties, resourceLoader, dataSource, flywayDataSource, fluentConfigurationCustomizers,
|
||||
javaMigrations, callbacks, new ResourceProviderCustomizer());
|
||||
javaMigrations, callbacks, new ResourceProviderCustomizer(), null);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -133,9 +135,15 @@ public class FlywayAutoConfiguration {
|
||||
@FlywayDataSource ObjectProvider<DataSource> flywayDataSource,
|
||||
ObjectProvider<FlywayConfigurationCustomizer> fluentConfigurationCustomizers,
|
||||
ObjectProvider<JavaMigration> javaMigrations, ObjectProvider<Callback> callbacks,
|
||||
ResourceProviderCustomizer resourceProviderCustomizer) {
|
||||
ResourceProviderCustomizer resourceProviderCustomizer,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
FluentConfiguration configuration = new FluentConfiguration(resourceLoader.getClassLoader());
|
||||
configureDataSource(configuration, properties, flywayDataSource.getIfAvailable(), dataSource.getIfUnique());
|
||||
JdbcConnectionDetails connectionDetails = (connectionDetailsProvider != null)
|
||||
? connectionDetailsProvider.getIfAvailable() : null;
|
||||
connectionDetails = (connectionDetails != null) ? connectionDetails
|
||||
: new FlywayPropertiesJdbcConnectionDetails(properties);
|
||||
configureDataSource(configuration, flywayDataSource.getIfAvailable(), dataSource.getIfUnique(),
|
||||
connectionDetails);
|
||||
configureProperties(configuration, properties);
|
||||
configureCallbacks(configuration, callbacks.orderedStream().toList());
|
||||
configureJavaMigrations(configuration, javaMigrations.orderedStream().toList());
|
||||
@@ -144,38 +152,41 @@ public class FlywayAutoConfiguration {
|
||||
return configuration.load();
|
||||
}
|
||||
|
||||
private void configureDataSource(FluentConfiguration configuration, FlywayProperties properties,
|
||||
DataSource flywayDataSource, DataSource dataSource) {
|
||||
DataSource migrationDataSource = getMigrationDataSource(properties, flywayDataSource, dataSource);
|
||||
private void configureDataSource(FluentConfiguration configuration, DataSource flywayDataSource,
|
||||
DataSource dataSource, JdbcConnectionDetails connectionDetails) {
|
||||
DataSource migrationDataSource = getMigrationDataSource(flywayDataSource, dataSource, connectionDetails);
|
||||
configuration.dataSource(migrationDataSource);
|
||||
}
|
||||
|
||||
private DataSource getMigrationDataSource(FlywayProperties properties, DataSource flywayDataSource,
|
||||
DataSource dataSource) {
|
||||
private DataSource getMigrationDataSource(DataSource flywayDataSource, DataSource dataSource,
|
||||
JdbcConnectionDetails connectionDetails) {
|
||||
if (flywayDataSource != null) {
|
||||
return flywayDataSource;
|
||||
}
|
||||
if (properties.getUrl() != null) {
|
||||
String url = connectionDetails.getJdbcUrl();
|
||||
if (url != null) {
|
||||
DataSourceBuilder<?> builder = DataSourceBuilder.create().type(SimpleDriverDataSource.class);
|
||||
builder.url(properties.getUrl());
|
||||
applyCommonBuilderProperties(properties, builder);
|
||||
builder.url(url);
|
||||
applyConnectionDetails(connectionDetails, builder);
|
||||
return builder.build();
|
||||
}
|
||||
if (properties.getUser() != null && dataSource != null) {
|
||||
String user = connectionDetails.getUsername();
|
||||
if (user != null && dataSource != null) {
|
||||
DataSourceBuilder<?> builder = DataSourceBuilder.derivedFrom(dataSource)
|
||||
.type(SimpleDriverDataSource.class);
|
||||
applyCommonBuilderProperties(properties, builder);
|
||||
applyConnectionDetails(connectionDetails, builder);
|
||||
return builder.build();
|
||||
}
|
||||
Assert.state(dataSource != null, "Flyway migration DataSource missing");
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
private void applyCommonBuilderProperties(FlywayProperties properties, DataSourceBuilder<?> builder) {
|
||||
builder.username(properties.getUser());
|
||||
builder.password(properties.getPassword());
|
||||
if (StringUtils.hasText(properties.getDriverClassName())) {
|
||||
builder.driverClassName(properties.getDriverClassName());
|
||||
private void applyConnectionDetails(JdbcConnectionDetails connectionDetails, DataSourceBuilder<?> builder) {
|
||||
builder.username(connectionDetails.getUsername());
|
||||
builder.password(connectionDetails.getPassword());
|
||||
String driverClassName = connectionDetails.getDriverClassName();
|
||||
if (StringUtils.hasText(driverClassName)) {
|
||||
builder.driverClassName(driverClassName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,6 +384,11 @@ public class FlywayAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnBean(JdbcConnectionDetails.class)
|
||||
private static final class JdbcConnectionDetailsCondition {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(prefix = "spring.flyway", name = "url")
|
||||
private static final class FlywayUrlCondition {
|
||||
|
||||
@@ -389,4 +405,37 @@ public class FlywayAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link FlywayProperties} to {@link JdbcConnectionDetails}.
|
||||
*/
|
||||
private static final class FlywayPropertiesJdbcConnectionDetails implements JdbcConnectionDetails {
|
||||
|
||||
private final FlywayProperties properties;
|
||||
|
||||
private FlywayPropertiesJdbcConnectionDetails(FlywayProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.properties.getUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.properties.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return this.properties.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDriverClassName() {
|
||||
return this.properties.getDriverClassName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.influx;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.influxdb.InfluxDB;
|
||||
import org.influxdb.impl.InfluxDBImpl;
|
||||
@@ -23,11 +25,15 @@ import org.influxdb.impl.InfluxDBImpl;
|
||||
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.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for InfluxDB.
|
||||
@@ -35,6 +41,9 @@ import org.springframework.context.annotation.Bean;
|
||||
* @author Sergey Kuptsov
|
||||
* @author Stephane Nicoll
|
||||
* @author Eddú Meléndez
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@@ -44,11 +53,14 @@ public class InfluxDbAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty("spring.influx.url")
|
||||
@Conditional(InfluxDBCondition.class)
|
||||
public InfluxDB influxDb(InfluxDbProperties properties, ObjectProvider<InfluxDbOkHttpClientBuilderProvider> builder,
|
||||
ObjectProvider<InfluxDbCustomizer> customizers) {
|
||||
InfluxDB influxDb = new InfluxDBImpl(properties.getUrl(), properties.getUser(), properties.getPassword(),
|
||||
determineBuilder(builder.getIfAvailable()));
|
||||
ObjectProvider<InfluxDbCustomizer> customizers,
|
||||
ObjectProvider<InfluxDbConnectionDetails> connectionDetailsProvider) {
|
||||
InfluxDbConnectionDetails connectionDetails = connectionDetailsProvider
|
||||
.getIfAvailable(() -> new PropertiesInfluxDbConnectionDetails(properties));
|
||||
InfluxDB influxDb = new InfluxDBImpl(connectionDetails.getUrl().toString(), connectionDetails.getUsername(),
|
||||
connectionDetails.getPassword(), determineBuilder(builder.getIfAvailable()));
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(influxDb));
|
||||
return influxDb;
|
||||
}
|
||||
@@ -60,4 +72,54 @@ public class InfluxDbAutoConfiguration {
|
||||
return new OkHttpClient.Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Condition} that matches when either {@code spring.influx.url} has been set
|
||||
* or there is an {@link InfluxDbConnectionDetails} bean.
|
||||
*/
|
||||
static final class InfluxDBCondition extends AnyNestedCondition {
|
||||
|
||||
InfluxDBCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(prefix = "spring.influx", name = "url")
|
||||
private static final class InfluxUrlCondition {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnBean(InfluxDbConnectionDetails.class)
|
||||
private static final class InfluxDbConnectionDetailsCondition {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link InfluxDbProperties} to {@link InfluxDbConnectionDetails}.
|
||||
*/
|
||||
static class PropertiesInfluxDbConnectionDetails implements InfluxDbConnectionDetails {
|
||||
|
||||
private final InfluxDbProperties properties;
|
||||
|
||||
PropertiesInfluxDbConnectionDetails(InfluxDbProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUrl() {
|
||||
return URI.create(this.properties.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.properties.getUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.properties.getPassword();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.influx;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to an InfluxDB service.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface InfluxDbConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* URL of the InfluxDB instance to which to connect.
|
||||
* @return the URL of the InfluxDB instance to which to connect
|
||||
*/
|
||||
URI getUrl();
|
||||
|
||||
/**
|
||||
* Login user.
|
||||
* @return the login user or {@code null}
|
||||
*/
|
||||
String getUsername();
|
||||
|
||||
/**
|
||||
* Login password.
|
||||
* @return the login password or {@code null}
|
||||
*/
|
||||
String getPassword();
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* 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.
|
||||
@@ -24,10 +24,13 @@ import com.zaxxer.hikari.HikariDataSource;
|
||||
import oracle.jdbc.OracleConnection;
|
||||
import oracle.ucp.jdbc.PoolDataSourceImpl;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
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.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -40,6 +43,8 @@ import org.springframework.util.StringUtils;
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Fabio Grassi
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
abstract class DataSourceConfiguration {
|
||||
|
||||
@@ -48,6 +53,17 @@ abstract class DataSourceConfiguration {
|
||||
return (T) properties.initializeDataSourceBuilder().type(type).build();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <T> T createDataSource(JdbcConnectionDetails connectionDetails, Class<? extends DataSource> type,
|
||||
ClassLoader classLoader) {
|
||||
return (T) DataSourceBuilder.create(classLoader)
|
||||
.url(connectionDetails.getJdbcUrl())
|
||||
.username(connectionDetails.getUsername())
|
||||
.password(connectionDetails.getPassword())
|
||||
.type(type)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tomcat Pool DataSource configuration.
|
||||
*/
|
||||
@@ -58,13 +74,26 @@ abstract class DataSourceConfiguration {
|
||||
matchIfMissing = true)
|
||||
static class Tomcat {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(JdbcConnectionDetails.class)
|
||||
static TomcatJdbcConnectionDetailsBeanPostProcessor tomcatJdbcConnectionDetailsBeanPostProcessor(
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
return new TomcatJdbcConnectionDetailsBeanPostProcessor(connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.datasource.tomcat")
|
||||
org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) {
|
||||
org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(properties,
|
||||
org.apache.tomcat.jdbc.pool.DataSource.class);
|
||||
DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl());
|
||||
String validationQuery = databaseDriver.getValidationQuery();
|
||||
org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable();
|
||||
Class<? extends DataSource> dataSourceType = org.apache.tomcat.jdbc.pool.DataSource.class;
|
||||
org.apache.tomcat.jdbc.pool.DataSource dataSource = (connectionDetails != null)
|
||||
? createDataSource(connectionDetails, dataSourceType, properties.getClassLoader())
|
||||
: createDataSource(properties, dataSourceType);
|
||||
String validationQuery;
|
||||
String url = (connectionDetails != null) ? connectionDetails.getJdbcUrl() : properties.determineUrl();
|
||||
DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(url);
|
||||
validationQuery = databaseDriver.getValidationQuery();
|
||||
if (validationQuery != null) {
|
||||
dataSource.setTestOnBorrow(true);
|
||||
dataSource.setValidationQuery(validationQuery);
|
||||
@@ -84,10 +113,21 @@ abstract class DataSourceConfiguration {
|
||||
matchIfMissing = true)
|
||||
static class Hikari {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(JdbcConnectionDetails.class)
|
||||
static HikariJdbcConnectionDetailsBeanPostProcessor jdbcConnectionDetailsHikariBeanPostProcessor(
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
return new HikariJdbcConnectionDetailsBeanPostProcessor(connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.datasource.hikari")
|
||||
HikariDataSource dataSource(DataSourceProperties properties) {
|
||||
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
|
||||
HikariDataSource dataSource(DataSourceProperties properties,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable();
|
||||
HikariDataSource dataSource = (connectionDetails != null)
|
||||
? createDataSource(connectionDetails, HikariDataSource.class, properties.getClassLoader())
|
||||
: createDataSource(properties, HikariDataSource.class);
|
||||
if (StringUtils.hasText(properties.getName())) {
|
||||
dataSource.setPoolName(properties.getName());
|
||||
}
|
||||
@@ -106,10 +146,22 @@ abstract class DataSourceConfiguration {
|
||||
matchIfMissing = true)
|
||||
static class Dbcp2 {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(JdbcConnectionDetails.class)
|
||||
static Dbcp2JdbcConnectionDetailsBeanPostProcessor dbcp2JdbcConnectionDetailsBeanPostProcessor(
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
return new Dbcp2JdbcConnectionDetailsBeanPostProcessor(connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.datasource.dbcp2")
|
||||
org.apache.commons.dbcp2.BasicDataSource dataSource(DataSourceProperties properties) {
|
||||
return createDataSource(properties, org.apache.commons.dbcp2.BasicDataSource.class);
|
||||
org.apache.commons.dbcp2.BasicDataSource dataSource(DataSourceProperties properties,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable();
|
||||
Class<? extends DataSource> dataSourceType = org.apache.commons.dbcp2.BasicDataSource.class;
|
||||
return (connectionDetails != null)
|
||||
? createDataSource(connectionDetails, dataSourceType, properties.getClassLoader())
|
||||
: createDataSource(properties, dataSourceType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -124,10 +176,21 @@ abstract class DataSourceConfiguration {
|
||||
matchIfMissing = true)
|
||||
static class OracleUcp {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(JdbcConnectionDetails.class)
|
||||
static OracleUcpJdbcConnectionDetailsBeanPostProcessor oracleUcpJdbcConnectionDetailsBeanPostProcessor(
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
return new OracleUcpJdbcConnectionDetailsBeanPostProcessor(connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.datasource.oracleucp")
|
||||
PoolDataSourceImpl dataSource(DataSourceProperties properties) throws SQLException {
|
||||
PoolDataSourceImpl dataSource = createDataSource(properties, PoolDataSourceImpl.class);
|
||||
PoolDataSourceImpl dataSource(DataSourceProperties properties,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) throws SQLException {
|
||||
JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable();
|
||||
PoolDataSourceImpl dataSource = (connectionDetails != null)
|
||||
? createDataSource(connectionDetails, PoolDataSourceImpl.class, properties.getClassLoader())
|
||||
: createDataSource(properties, PoolDataSourceImpl.class);
|
||||
dataSource.setValidateConnectionOnBorrow(true);
|
||||
if (StringUtils.hasText(properties.getName())) {
|
||||
dataSource.setConnectionPoolName(properties.getName());
|
||||
@@ -146,7 +209,17 @@ abstract class DataSourceConfiguration {
|
||||
static class Generic {
|
||||
|
||||
@Bean
|
||||
DataSource dataSource(DataSourceProperties properties) {
|
||||
DataSource dataSource(DataSourceProperties properties,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable();
|
||||
if (connectionDetails != null) {
|
||||
return DataSourceBuilder.create(properties.getClassLoader())
|
||||
.url(connectionDetails.getJdbcUrl())
|
||||
.username(connectionDetails.getUsername())
|
||||
.password(connectionDetails.getPassword())
|
||||
.type(properties.getType())
|
||||
.build();
|
||||
}
|
||||
return properties.initializeDataSourceBuilder().build();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
|
||||
/**
|
||||
* Post-processes beans of type {@link BasicDataSource} and name 'dataSource' to apply the
|
||||
* values from {@link JdbcConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class Dbcp2JdbcConnectionDetailsBeanPostProcessor extends JdbcConnectionDetailsBeanPostProcessor<BasicDataSource> {
|
||||
|
||||
Dbcp2JdbcConnectionDetailsBeanPostProcessor(ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
super(BasicDataSource.class, connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object processDataSource(BasicDataSource dataSource, JdbcConnectionDetails connectionDetails) {
|
||||
dataSource.setUrl(connectionDetails.getJdbcUrl());
|
||||
dataSource.setUsername(connectionDetails.getUsername());
|
||||
dataSource.setPassword(connectionDetails.getPassword());
|
||||
dataSource.setDriverClassName(connectionDetails.getDriverClassName());
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
|
||||
/**
|
||||
* Post-processes beans of type {@link HikariDataSource} and name 'dataSource' to apply
|
||||
* the values from {@link JdbcConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class HikariJdbcConnectionDetailsBeanPostProcessor extends JdbcConnectionDetailsBeanPostProcessor<HikariDataSource> {
|
||||
|
||||
HikariJdbcConnectionDetailsBeanPostProcessor(ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
super(HikariDataSource.class, connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object processDataSource(HikariDataSource dataSource, JdbcConnectionDetails connectionDetails) {
|
||||
dataSource.setJdbcUrl(connectionDetails.getJdbcUrl());
|
||||
dataSource.setUsername(connectionDetails.getUsername());
|
||||
dataSource.setPassword(connectionDetails.getPassword());
|
||||
dataSource.setDriverClassName(connectionDetails.getDriverClassName());
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to an SQL service using JDBC.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface JdbcConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* Hostname for the database.
|
||||
* @return the username for the database
|
||||
*/
|
||||
String getUsername();
|
||||
|
||||
/**
|
||||
* Password for the database.
|
||||
* @return the password for the database
|
||||
*/
|
||||
String getPassword();
|
||||
|
||||
/**
|
||||
* JDBC url for the database.
|
||||
* @return the JDBC url for the database
|
||||
*/
|
||||
String getJdbcUrl();
|
||||
|
||||
/**
|
||||
* The name of the JDBC driver class. Defaults to the class name of the driver
|
||||
* specified in the JDBC URL.
|
||||
* @return the JDBC driver class name
|
||||
* @see #getJdbcUrl()
|
||||
* @see DatabaseDriver#fromJdbcUrl(String)
|
||||
* @see DatabaseDriver#getDriverClassName()
|
||||
*/
|
||||
default String getDriverClassName() {
|
||||
return DatabaseDriver.fromJdbcUrl(getJdbcUrl()).getDriverClassName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the XA DataSource class. Defaults to the class name from the
|
||||
* driver specified in the JDBC URL.
|
||||
* @return the XA DataSource class name
|
||||
* @see #getJdbcUrl()
|
||||
* @see DatabaseDriver#fromJdbcUrl(String)
|
||||
* @see DatabaseDriver#getXaDataSourceClassName()
|
||||
*/
|
||||
default String getXaDataSourceClassName() {
|
||||
return DatabaseDriver.fromJdbcUrl(getJdbcUrl()).getXaDataSourceClassName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
|
||||
/**
|
||||
* Abstract base class for DataSource bean post processors which apply values from
|
||||
* {@link JdbcConnectionDetails}. Acts on beans named 'dataSource' of type {@code T}.
|
||||
*
|
||||
* @param <T> type of the datasource
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class JdbcConnectionDetailsBeanPostProcessor<T> implements BeanPostProcessor, PriorityOrdered {
|
||||
|
||||
private final Class<T> dataSourceClass;
|
||||
|
||||
private final ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider;
|
||||
|
||||
JdbcConnectionDetailsBeanPostProcessor(Class<T> dataSourceClass,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
this.dataSourceClass = dataSourceClass;
|
||||
this.connectionDetailsProvider = connectionDetailsProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (this.dataSourceClass.isAssignableFrom(bean.getClass()) && "dataSource".equals(beanName)) {
|
||||
JdbcConnectionDetails connectionDetails = this.connectionDetailsProvider.getObject();
|
||||
return processDataSource((T) bean, connectionDetails);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
protected abstract Object processDataSource(T dataSource, JdbcConnectionDetails connectionDetails);
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
// Runs after ConfigurationPropertiesBindingPostProcessor
|
||||
return Ordered.HIGHEST_PRECEDENCE + 2;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import oracle.ucp.jdbc.PoolDataSourceImpl;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
|
||||
/**
|
||||
* Post-processes beans of type {@link PoolDataSourceImpl} and name 'dataSource' to apply
|
||||
* the values from {@link JdbcConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class OracleUcpJdbcConnectionDetailsBeanPostProcessor
|
||||
extends JdbcConnectionDetailsBeanPostProcessor<PoolDataSourceImpl> {
|
||||
|
||||
OracleUcpJdbcConnectionDetailsBeanPostProcessor(ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
super(PoolDataSourceImpl.class, connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object processDataSource(PoolDataSourceImpl dataSource, JdbcConnectionDetails connectionDetails) {
|
||||
try {
|
||||
dataSource.setURL(connectionDetails.getJdbcUrl());
|
||||
dataSource.setUser(connectionDetails.getUsername());
|
||||
dataSource.setPassword(connectionDetails.getPassword());
|
||||
dataSource.setConnectionFactoryClassName(connectionDetails.getDriverClassName());
|
||||
return dataSource;
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
throw new RuntimeException("Failed to set URL / user / password of datasource", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import org.apache.tomcat.jdbc.pool.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
|
||||
/**
|
||||
* Post-processes beans of type {@link DataSource} and name 'dataSource' to apply the
|
||||
* values from {@link JdbcConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class TomcatJdbcConnectionDetailsBeanPostProcessor extends JdbcConnectionDetailsBeanPostProcessor<DataSource> {
|
||||
|
||||
TomcatJdbcConnectionDetailsBeanPostProcessor(ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
super(DataSource.class, connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object processDataSource(DataSource dataSource, JdbcConnectionDetails connectionDetails) {
|
||||
dataSource.setUrl(connectionDetails.getJdbcUrl());
|
||||
dataSource.setUsername(connectionDetails.getUsername());
|
||||
dataSource.setPassword(connectionDetails.getPassword());
|
||||
dataSource.setDriverClassName(connectionDetails.getDriverClassName());
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -40,7 +40,6 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyN
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
import org.springframework.boot.jdbc.XADataSourceWrapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
@@ -54,6 +53,8 @@ import org.springframework.util.StringUtils;
|
||||
* @author Phillip Webb
|
||||
* @author Josh Long
|
||||
* @author Madhura Bhave
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@AutoConfiguration(before = DataSourceAutoConfiguration.class)
|
||||
@@ -67,8 +68,10 @@ public class XADataSourceAutoConfiguration implements BeanClassLoaderAware {
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource(XADataSourceWrapper wrapper, DataSourceProperties properties,
|
||||
ObjectProvider<XADataSource> xaDataSource) throws Exception {
|
||||
return wrapper.wrapDataSource(xaDataSource.getIfAvailable(() -> createXaDataSource(properties)));
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetails, ObjectProvider<XADataSource> xaDataSource)
|
||||
throws Exception {
|
||||
return wrapper.wrapDataSource(xaDataSource.getIfAvailable(() -> createXaDataSource(properties,
|
||||
connectionDetails.getIfAvailable(() -> new PropertiesJdbcConnectionDetails(properties)))));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,14 +79,11 @@ public class XADataSourceAutoConfiguration implements BeanClassLoaderAware {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
private XADataSource createXaDataSource(DataSourceProperties properties) {
|
||||
String className = properties.getXa().getDataSourceClassName();
|
||||
if (!StringUtils.hasLength(className)) {
|
||||
className = DatabaseDriver.fromJdbcUrl(properties.determineUrl()).getXaDataSourceClassName();
|
||||
}
|
||||
private XADataSource createXaDataSource(DataSourceProperties properties, JdbcConnectionDetails connectionDetails) {
|
||||
String className = connectionDetails.getXaDataSourceClassName();
|
||||
Assert.state(StringUtils.hasLength(className), "No XA DataSource class name specified");
|
||||
XADataSource dataSource = createXaDataSourceInstance(className);
|
||||
bindXaProperties(dataSource, properties);
|
||||
bindXaProperties(dataSource, properties, connectionDetails);
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@@ -99,18 +99,19 @@ public class XADataSourceAutoConfiguration implements BeanClassLoaderAware {
|
||||
}
|
||||
}
|
||||
|
||||
private void bindXaProperties(XADataSource target, DataSourceProperties dataSourceProperties) {
|
||||
Binder binder = new Binder(getBinderSource(dataSourceProperties));
|
||||
private void bindXaProperties(XADataSource target, DataSourceProperties dataSourceProperties,
|
||||
JdbcConnectionDetails connectionDetails) {
|
||||
Binder binder = new Binder(getBinderSource(dataSourceProperties, connectionDetails));
|
||||
binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(target));
|
||||
}
|
||||
|
||||
private ConfigurationPropertySource getBinderSource(DataSourceProperties dataSourceProperties) {
|
||||
Map<Object, Object> properties = new HashMap<>();
|
||||
properties.putAll(dataSourceProperties.getXa().getProperties());
|
||||
properties.computeIfAbsent("user", (key) -> dataSourceProperties.determineUsername());
|
||||
properties.computeIfAbsent("password", (key) -> dataSourceProperties.determinePassword());
|
||||
private ConfigurationPropertySource getBinderSource(DataSourceProperties dataSourceProperties,
|
||||
JdbcConnectionDetails connectionDetails) {
|
||||
Map<Object, Object> properties = new HashMap<>(dataSourceProperties.getXa().getProperties());
|
||||
properties.computeIfAbsent("user", (key) -> connectionDetails.getUsername());
|
||||
properties.computeIfAbsent("password", (key) -> connectionDetails.getPassword());
|
||||
try {
|
||||
properties.computeIfAbsent("url", (key) -> dataSourceProperties.determineUrl());
|
||||
properties.computeIfAbsent("url", (key) -> connectionDetails.getJdbcUrl());
|
||||
}
|
||||
catch (DataSourceBeanCreationException ex) {
|
||||
// Continue as not all XA DataSource's require a URL
|
||||
@@ -121,4 +122,45 @@ public class XADataSourceAutoConfiguration implements BeanClassLoaderAware {
|
||||
return source.withAliases(aliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link DataSourceProperties} to {@link JdbcConnectionDetails}.
|
||||
*/
|
||||
private static class PropertiesJdbcConnectionDetails implements JdbcConnectionDetails {
|
||||
|
||||
private final DataSourceProperties properties;
|
||||
|
||||
PropertiesJdbcConnectionDetails(DataSourceProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.properties.determineUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.properties.determinePassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return this.properties.determineUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDriverClassName() {
|
||||
return (this.properties.getDriverClassName() != null) ? this.properties.getDriverClassName()
|
||||
: JdbcConnectionDetails.super.getDriverClassName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getXaDataSourceClassName() {
|
||||
return (this.properties.getXa().getDataSourceClassName() != null)
|
||||
? this.properties.getXa().getDataSourceClassName()
|
||||
: JdbcConnectionDetails.super.getXaDataSourceClassName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,12 @@ package org.springframework.boot.autoconfigure.kafka;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.kafka.clients.CommonClientConfigs;
|
||||
import org.apache.kafka.clients.consumer.ConsumerConfig;
|
||||
import org.apache.kafka.clients.producer.ProducerConfig;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
@@ -26,6 +32,7 @@ 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.kafka.KafkaConnectionDetails.Node;
|
||||
import org.springframework.boot.autoconfigure.kafka.KafkaProperties.Jaas;
|
||||
import org.springframework.boot.autoconfigure.kafka.KafkaProperties.Retry.Topic;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -56,6 +63,9 @@ import org.springframework.retry.backoff.SleepingBackOffPolicy;
|
||||
* @author Eddú Meléndez
|
||||
* @author Nakul Mishra
|
||||
* @author Tomaz Fernandes
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@@ -66,8 +76,12 @@ public class KafkaAutoConfiguration {
|
||||
|
||||
private final KafkaProperties properties;
|
||||
|
||||
public KafkaAutoConfiguration(KafkaProperties properties) {
|
||||
private final KafkaConnectionDetails connectionDetails;
|
||||
|
||||
KafkaAutoConfiguration(KafkaProperties properties, ObjectProvider<KafkaConnectionDetails> connectionDetails) {
|
||||
this.properties = properties;
|
||||
this.connectionDetails = connectionDetails
|
||||
.getIfAvailable(() -> new PropertiesKafkaConnectionDetails(properties));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -94,8 +108,9 @@ public class KafkaAutoConfiguration {
|
||||
@ConditionalOnMissingBean(ConsumerFactory.class)
|
||||
public DefaultKafkaConsumerFactory<?, ?> kafkaConsumerFactory(
|
||||
ObjectProvider<DefaultKafkaConsumerFactoryCustomizer> customizers) {
|
||||
DefaultKafkaConsumerFactory<Object, Object> factory = new DefaultKafkaConsumerFactory<>(
|
||||
this.properties.buildConsumerProperties());
|
||||
Map<String, Object> properties = this.properties.buildConsumerProperties();
|
||||
applyKafkaConnectionDetailsForConsumer(properties);
|
||||
DefaultKafkaConsumerFactory<Object, Object> factory = new DefaultKafkaConsumerFactory<>(properties);
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(factory));
|
||||
return factory;
|
||||
}
|
||||
@@ -104,8 +119,9 @@ public class KafkaAutoConfiguration {
|
||||
@ConditionalOnMissingBean(ProducerFactory.class)
|
||||
public DefaultKafkaProducerFactory<?, ?> kafkaProducerFactory(
|
||||
ObjectProvider<DefaultKafkaProducerFactoryCustomizer> customizers) {
|
||||
DefaultKafkaProducerFactory<?, ?> factory = new DefaultKafkaProducerFactory<>(
|
||||
this.properties.buildProducerProperties());
|
||||
Map<String, Object> properties = this.properties.buildProducerProperties();
|
||||
applyKafkaConnectionDetailsForProducer(properties);
|
||||
DefaultKafkaProducerFactory<?, ?> factory = new DefaultKafkaProducerFactory<>(properties);
|
||||
String transactionIdPrefix = this.properties.getProducer().getTransactionIdPrefix();
|
||||
if (transactionIdPrefix != null) {
|
||||
factory.setTransactionIdPrefix(transactionIdPrefix);
|
||||
@@ -140,7 +156,9 @@ public class KafkaAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public KafkaAdmin kafkaAdmin() {
|
||||
KafkaAdmin kafkaAdmin = new KafkaAdmin(this.properties.buildAdminProperties());
|
||||
Map<String, Object> properties = this.properties.buildAdminProperties();
|
||||
applyKafkaConnectionDetailsForAdmin(properties);
|
||||
KafkaAdmin kafkaAdmin = new KafkaAdmin(properties);
|
||||
KafkaProperties.Admin admin = this.properties.getAdmin();
|
||||
if (admin.getCloseTimeout() != null) {
|
||||
kafkaAdmin.setCloseTimeout((int) admin.getCloseTimeout().getSeconds());
|
||||
@@ -168,6 +186,34 @@ public class KafkaAutoConfiguration {
|
||||
return builder.create(kafkaTemplate);
|
||||
}
|
||||
|
||||
private void applyKafkaConnectionDetailsForConsumer(Map<String, Object> properties) {
|
||||
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
nodesToStringList(this.connectionDetails.getConsumerBootstrapNodes()));
|
||||
if (!(this.connectionDetails instanceof PropertiesKafkaConnectionDetails)) {
|
||||
properties.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT");
|
||||
}
|
||||
}
|
||||
|
||||
private void applyKafkaConnectionDetailsForProducer(Map<String, Object> properties) {
|
||||
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
nodesToStringList(this.connectionDetails.getProducerBootstrapNodes()));
|
||||
if (!(this.connectionDetails instanceof PropertiesKafkaConnectionDetails)) {
|
||||
properties.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT");
|
||||
}
|
||||
}
|
||||
|
||||
private void applyKafkaConnectionDetailsForAdmin(Map<String, Object> properties) {
|
||||
properties.put(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG,
|
||||
nodesToStringList(this.connectionDetails.getAdminBootstrapNodes()));
|
||||
if (!(this.connectionDetails instanceof PropertiesKafkaConnectionDetails)) {
|
||||
properties.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT");
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> nodesToStringList(List<Node> nodes) {
|
||||
return nodes.stream().map((node) -> node.host() + ":" + node.port()).toList();
|
||||
}
|
||||
|
||||
private static void setBackOffPolicy(RetryTopicConfigurationBuilder builder, Topic retryTopic) {
|
||||
long delay = (retryTopic.getDelay() != null) ? retryTopic.getDelay().toMillis() : 0;
|
||||
if (delay > 0) {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.kafka;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to a Kafka service.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface KafkaConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* Returns the list of bootstrap nodes.
|
||||
* @return the list of bootstrap nodes
|
||||
*/
|
||||
List<Node> getBootstrapNodes();
|
||||
|
||||
/**
|
||||
* Returns the list of bootstrap nodes used for consumers.
|
||||
* @return the list of bootstrap nodes used for consumers
|
||||
*/
|
||||
default List<Node> getConsumerBootstrapNodes() {
|
||||
return getBootstrapNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of bootstrap nodes used for producers.
|
||||
* @return the list of bootstrap nodes used for producers
|
||||
*/
|
||||
default List<Node> getProducerBootstrapNodes() {
|
||||
return getBootstrapNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of bootstrap nodes used for the admin.
|
||||
* @return the list of bootstrap nodes used for the admin
|
||||
*/
|
||||
default List<Node> getAdminBootstrapNodes() {
|
||||
return getBootstrapNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of bootstrap nodes used for Kafka Streams.
|
||||
* @return the list of bootstrap nodes used for Kafka Streams
|
||||
*/
|
||||
default List<Node> getStreamsBootstrapNodes() {
|
||||
return getBootstrapNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* A Kafka node.
|
||||
*
|
||||
* @param host the hostname
|
||||
* @param port the port
|
||||
*/
|
||||
record Node(String host, int port) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.kafka;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.kafka.clients.CommonClientConfigs;
|
||||
import org.apache.kafka.clients.consumer.ConsumerConfig;
|
||||
import org.apache.kafka.streams.StreamsBuilder;
|
||||
import org.apache.kafka.streams.StreamsConfig;
|
||||
|
||||
@@ -27,6 +30,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
||||
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.kafka.KafkaConnectionDetails.Node;
|
||||
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -42,6 +46,8 @@ import org.springframework.kafka.core.CleanupConfig;
|
||||
* @author Gary Russell
|
||||
* @author Stephane Nicoll
|
||||
* @author Eddú Meléndez
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(StreamsBuilder.class)
|
||||
@@ -56,17 +62,21 @@ class KafkaStreamsAnnotationDrivenConfiguration {
|
||||
|
||||
@ConditionalOnMissingBean
|
||||
@Bean(KafkaStreamsDefaultConfiguration.DEFAULT_STREAMS_CONFIG_BEAN_NAME)
|
||||
KafkaStreamsConfiguration defaultKafkaStreamsConfig(Environment environment) {
|
||||
Map<String, Object> streamsProperties = this.properties.buildStreamsProperties();
|
||||
KafkaStreamsConfiguration defaultKafkaStreamsConfig(Environment environment,
|
||||
ObjectProvider<KafkaConnectionDetails> connectionDetailsProvider) {
|
||||
KafkaConnectionDetails connectionDetails = connectionDetailsProvider
|
||||
.getIfAvailable(() -> new PropertiesKafkaConnectionDetails(this.properties));
|
||||
Map<String, Object> properties = this.properties.buildStreamsProperties();
|
||||
applyKafkaConnectionDetailsForStreams(connectionDetails, properties);
|
||||
if (this.properties.getStreams().getApplicationId() == null) {
|
||||
String applicationName = environment.getProperty("spring.application.name");
|
||||
if (applicationName == null) {
|
||||
throw new InvalidConfigurationPropertyValueException("spring.kafka.streams.application-id", null,
|
||||
"This property is mandatory and fallback 'spring.application.name' is not set either.");
|
||||
}
|
||||
streamsProperties.put(StreamsConfig.APPLICATION_ID_CONFIG, applicationName);
|
||||
properties.put(StreamsConfig.APPLICATION_ID_CONFIG, applicationName);
|
||||
}
|
||||
return new KafkaStreamsConfiguration(streamsProperties);
|
||||
return new KafkaStreamsConfiguration(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -77,6 +87,19 @@ class KafkaStreamsAnnotationDrivenConfiguration {
|
||||
return new KafkaStreamsFactoryBeanConfigurer(this.properties, factoryBean);
|
||||
}
|
||||
|
||||
private void applyKafkaConnectionDetailsForStreams(KafkaConnectionDetails connectionDetails,
|
||||
Map<String, Object> properties) {
|
||||
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
nodesToStringList(connectionDetails.getStreamsBootstrapNodes()));
|
||||
if (!(connectionDetails instanceof PropertiesKafkaConnectionDetails)) {
|
||||
properties.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT");
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> nodesToStringList(List<Node> nodes) {
|
||||
return nodes.stream().map((node) -> node.host() + ":" + node.port()).toList();
|
||||
}
|
||||
|
||||
// Separate class required to avoid BeanCurrentlyInCreationException
|
||||
static class KafkaStreamsFactoryBeanConfigurer implements InitializingBean {
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.kafka;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Adapts {@link KafkaProperties} to {@link KafkaConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class PropertiesKafkaConnectionDetails implements KafkaConnectionDetails {
|
||||
|
||||
private final int DEFAULT_PORT = 9092;
|
||||
|
||||
private final KafkaProperties properties;
|
||||
|
||||
PropertiesKafkaConnectionDetails(KafkaProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getBootstrapNodes() {
|
||||
return asNodes(this.properties.getBootstrapServers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getConsumerBootstrapNodes() {
|
||||
return bootstrapNodes(this.properties.getConsumer().getBootstrapServers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getProducerBootstrapNodes() {
|
||||
return bootstrapNodes(this.properties.getProducer().getBootstrapServers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getStreamsBootstrapNodes() {
|
||||
return bootstrapNodes(this.properties.getStreams().getBootstrapServers());
|
||||
}
|
||||
|
||||
private List<Node> bootstrapNodes(List<String> bootstrapServers) {
|
||||
return (bootstrapServers != null) ? asNodes(bootstrapServers) : getBootstrapNodes();
|
||||
}
|
||||
|
||||
private List<Node> asNodes(List<String> bootstrapServers) {
|
||||
return bootstrapServers.stream().map(this::asNode).toList();
|
||||
}
|
||||
|
||||
private Node asNode(String bootstrapNode) {
|
||||
int separatorIndex = bootstrapNode.indexOf(':');
|
||||
if (separatorIndex == -1) {
|
||||
return new Node(bootstrapNode, this.DEFAULT_PORT);
|
||||
}
|
||||
return new Node(bootstrapNode.substring(0, separatorIndex),
|
||||
Integer.parseInt(bootstrapNode.substring(separatorIndex + 1)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,6 +32,7 @@ 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.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseAutoConfigurationRuntimeHints;
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseDataSourceCondition;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
@@ -61,6 +62,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author András Deák
|
||||
* @author Ferenc Gratzer
|
||||
* @author Evgeniy Cheban
|
||||
* @author Moritz Halbritter
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@AutoConfiguration(after = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
|
||||
@@ -83,38 +85,34 @@ public class LiquibaseAutoConfiguration {
|
||||
@EnableConfigurationProperties(LiquibaseProperties.class)
|
||||
public static class LiquibaseConfiguration {
|
||||
|
||||
private final LiquibaseProperties properties;
|
||||
|
||||
public LiquibaseConfiguration(LiquibaseProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpringLiquibase liquibase(ObjectProvider<DataSource> dataSource,
|
||||
@LiquibaseDataSource ObjectProvider<DataSource> liquibaseDataSource) {
|
||||
@LiquibaseDataSource ObjectProvider<DataSource> liquibaseDataSource, LiquibaseProperties properties,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetails) {
|
||||
SpringLiquibase liquibase = createSpringLiquibase(liquibaseDataSource.getIfAvailable(),
|
||||
dataSource.getIfUnique());
|
||||
liquibase.setChangeLog(this.properties.getChangeLog());
|
||||
liquibase.setClearCheckSums(this.properties.isClearChecksums());
|
||||
liquibase.setContexts(this.properties.getContexts());
|
||||
liquibase.setDefaultSchema(this.properties.getDefaultSchema());
|
||||
liquibase.setLiquibaseSchema(this.properties.getLiquibaseSchema());
|
||||
liquibase.setLiquibaseTablespace(this.properties.getLiquibaseTablespace());
|
||||
liquibase.setDatabaseChangeLogTable(this.properties.getDatabaseChangeLogTable());
|
||||
liquibase.setDatabaseChangeLogLockTable(this.properties.getDatabaseChangeLogLockTable());
|
||||
liquibase.setDropFirst(this.properties.isDropFirst());
|
||||
liquibase.setShouldRun(this.properties.isEnabled());
|
||||
liquibase.setLabelFilter(this.properties.getLabelFilter());
|
||||
liquibase.setChangeLogParameters(this.properties.getParameters());
|
||||
liquibase.setRollbackFile(this.properties.getRollbackFile());
|
||||
liquibase.setTestRollbackOnUpdate(this.properties.isTestRollbackOnUpdate());
|
||||
liquibase.setTag(this.properties.getTag());
|
||||
dataSource.getIfUnique(),
|
||||
connectionDetails.getIfAvailable(() -> new LiquibasePropertiesJdbcConnectionDetails(properties)));
|
||||
liquibase.setChangeLog(properties.getChangeLog());
|
||||
liquibase.setClearCheckSums(properties.isClearChecksums());
|
||||
liquibase.setContexts(properties.getContexts());
|
||||
liquibase.setDefaultSchema(properties.getDefaultSchema());
|
||||
liquibase.setLiquibaseSchema(properties.getLiquibaseSchema());
|
||||
liquibase.setLiquibaseTablespace(properties.getLiquibaseTablespace());
|
||||
liquibase.setDatabaseChangeLogTable(properties.getDatabaseChangeLogTable());
|
||||
liquibase.setDatabaseChangeLogLockTable(properties.getDatabaseChangeLogLockTable());
|
||||
liquibase.setDropFirst(properties.isDropFirst());
|
||||
liquibase.setShouldRun(properties.isEnabled());
|
||||
liquibase.setLabelFilter(properties.getLabelFilter());
|
||||
liquibase.setChangeLogParameters(properties.getParameters());
|
||||
liquibase.setRollbackFile(properties.getRollbackFile());
|
||||
liquibase.setTestRollbackOnUpdate(properties.isTestRollbackOnUpdate());
|
||||
liquibase.setTag(properties.getTag());
|
||||
return liquibase;
|
||||
}
|
||||
|
||||
private SpringLiquibase createSpringLiquibase(DataSource liquibaseDataSource, DataSource dataSource) {
|
||||
LiquibaseProperties properties = this.properties;
|
||||
DataSource migrationDataSource = getMigrationDataSource(liquibaseDataSource, dataSource, properties);
|
||||
private SpringLiquibase createSpringLiquibase(DataSource liquibaseDataSource, DataSource dataSource,
|
||||
JdbcConnectionDetails connectionDetails) {
|
||||
DataSource migrationDataSource = getMigrationDataSource(liquibaseDataSource, dataSource, connectionDetails);
|
||||
SpringLiquibase liquibase = (migrationDataSource == liquibaseDataSource
|
||||
|| migrationDataSource == dataSource) ? new SpringLiquibase()
|
||||
: new DataSourceClosingSpringLiquibase();
|
||||
@@ -123,31 +121,34 @@ public class LiquibaseAutoConfiguration {
|
||||
}
|
||||
|
||||
private DataSource getMigrationDataSource(DataSource liquibaseDataSource, DataSource dataSource,
|
||||
LiquibaseProperties properties) {
|
||||
JdbcConnectionDetails connectionDetails) {
|
||||
if (liquibaseDataSource != null) {
|
||||
return liquibaseDataSource;
|
||||
}
|
||||
if (properties.getUrl() != null) {
|
||||
String url = connectionDetails.getJdbcUrl();
|
||||
if (url != null) {
|
||||
DataSourceBuilder<?> builder = DataSourceBuilder.create().type(SimpleDriverDataSource.class);
|
||||
builder.url(properties.getUrl());
|
||||
applyCommonBuilderProperties(properties, builder);
|
||||
builder.url(url);
|
||||
applyConnectionDetails(connectionDetails, builder);
|
||||
return builder.build();
|
||||
}
|
||||
if (properties.getUser() != null && dataSource != null) {
|
||||
String user = connectionDetails.getUsername();
|
||||
if (user != null && dataSource != null) {
|
||||
DataSourceBuilder<?> builder = DataSourceBuilder.derivedFrom(dataSource)
|
||||
.type(SimpleDriverDataSource.class);
|
||||
applyCommonBuilderProperties(properties, builder);
|
||||
applyConnectionDetails(connectionDetails, builder);
|
||||
return builder.build();
|
||||
}
|
||||
Assert.state(dataSource != null, "Liquibase migration DataSource missing");
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
private void applyCommonBuilderProperties(LiquibaseProperties properties, DataSourceBuilder<?> builder) {
|
||||
builder.username(properties.getUser());
|
||||
builder.password(properties.getPassword());
|
||||
if (StringUtils.hasText(properties.getDriverClassName())) {
|
||||
builder.driverClassName(properties.getDriverClassName());
|
||||
private void applyConnectionDetails(JdbcConnectionDetails connectionDetails, DataSourceBuilder<?> builder) {
|
||||
builder.username(connectionDetails.getUsername());
|
||||
builder.password(connectionDetails.getPassword());
|
||||
String driverClassName = connectionDetails.getDriverClassName();
|
||||
if (StringUtils.hasText(driverClassName)) {
|
||||
builder.driverClassName(driverClassName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +165,11 @@ public class LiquibaseAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnBean(JdbcConnectionDetails.class)
|
||||
private static final class JdbcConnectionDetailsCondition {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(prefix = "spring.liquibase", name = "url")
|
||||
private static final class LiquibaseUrlCondition {
|
||||
|
||||
@@ -180,4 +186,37 @@ public class LiquibaseAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link LiquibaseProperties} to {@link JdbcConnectionDetails}.
|
||||
*/
|
||||
private static final class LiquibasePropertiesJdbcConnectionDetails implements JdbcConnectionDetails {
|
||||
|
||||
private final LiquibaseProperties properties;
|
||||
|
||||
private LiquibasePropertiesJdbcConnectionDetails(LiquibaseProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.properties.getUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.properties.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return this.properties.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDriverClassName() {
|
||||
return this.properties.getDriverClassName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -62,8 +62,12 @@ public class MongoAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
MongoPropertiesClientSettingsBuilderCustomizer mongoPropertiesCustomizer(MongoProperties properties) {
|
||||
return new MongoPropertiesClientSettingsBuilderCustomizer(properties);
|
||||
StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties,
|
||||
ObjectProvider<MongoConnectionDetails> connectionDetailsProvider) {
|
||||
MongoConnectionDetails connectionDetails = connectionDetailsProvider
|
||||
.getIfAvailable(() -> new PropertiesMongoConnectionDetails(properties));
|
||||
return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails.getConnectionString(),
|
||||
properties.getUuidRepresentation());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.mongo;
|
||||
|
||||
import com.mongodb.ConnectionString;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to a MongoDB service.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface MongoConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* The {@link ConnectionString} for MongoDB.
|
||||
* @return the connection string
|
||||
*/
|
||||
ConnectionString getConnectionString();
|
||||
|
||||
/**
|
||||
* GridFS configuration.
|
||||
* @return the GridFS configuration or {@code null}
|
||||
*/
|
||||
default GridFs getGridFs() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* GridFS configuration.
|
||||
*/
|
||||
interface GridFs {
|
||||
|
||||
/**
|
||||
* GridFS database name.
|
||||
* @return the GridFS database name or {@code null}
|
||||
*/
|
||||
String getDatabase();
|
||||
|
||||
/**
|
||||
* GridFS bucket name.
|
||||
* @return the GridFS bucket name or {@code null}
|
||||
*/
|
||||
String getBucket();
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link GridFs} instance.
|
||||
* @param database the database
|
||||
* @param bucket the bucket name
|
||||
* @return a new {@link GridFs} instance
|
||||
*/
|
||||
static GridFs of(String database, String bucket) {
|
||||
return new GridFs() {
|
||||
|
||||
@Override
|
||||
public String getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBucket() {
|
||||
return bucket;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -34,7 +34,10 @@ import org.springframework.util.CollectionUtils;
|
||||
* @author Scott Frederick
|
||||
* @author Safeer Ansari
|
||||
* @since 2.4.0
|
||||
* @deprecated since 3.1.0 in favor of
|
||||
* {@link StandardMongoClientSettingsBuilderCustomizer}
|
||||
*/
|
||||
@Deprecated(since = "3.1.0", forRemoval = true)
|
||||
public class MongoPropertiesClientSettingsBuilderCustomizer implements MongoClientSettingsBuilderCustomizer, Ordered {
|
||||
|
||||
private final MongoProperties properties;
|
||||
|
||||
@@ -69,8 +69,12 @@ public class MongoReactiveAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
MongoPropertiesClientSettingsBuilderCustomizer mongoPropertiesCustomizer(MongoProperties properties) {
|
||||
return new MongoPropertiesClientSettingsBuilderCustomizer(properties);
|
||||
StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties,
|
||||
ObjectProvider<MongoConnectionDetails> connectionDetailsProvider) {
|
||||
MongoConnectionDetails connectionDetails = connectionDetailsProvider
|
||||
.getIfAvailable(() -> new PropertiesMongoConnectionDetails(properties));
|
||||
return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails.getConnectionString(),
|
||||
properties.getUuidRepresentation());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.mongo;
|
||||
|
||||
import com.mongodb.ConnectionString;
|
||||
|
||||
/**
|
||||
* Adapts {@link MongoProperties} to {@link MongoConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class PropertiesMongoConnectionDetails implements MongoConnectionDetails {
|
||||
|
||||
private final MongoProperties properties;
|
||||
|
||||
public PropertiesMongoConnectionDetails(MongoProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionString getConnectionString() {
|
||||
// mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database.collection][?options]]
|
||||
if (this.properties.getUri() != null) {
|
||||
return new ConnectionString(this.properties.getUri());
|
||||
}
|
||||
StringBuilder builder = new StringBuilder("mongodb://");
|
||||
if (this.properties.getUsername() != null) {
|
||||
builder.append(this.properties.getUsername());
|
||||
builder.append(":");
|
||||
builder.append(this.properties.getPassword());
|
||||
builder.append("@");
|
||||
}
|
||||
builder.append((this.properties.getHost() != null) ? this.properties.getHost() : "localhost");
|
||||
if (this.properties.getPort() != null) {
|
||||
builder.append(":");
|
||||
builder.append(this.properties.getPort());
|
||||
}
|
||||
if (this.properties.getAdditionalHosts() != null) {
|
||||
builder.append(String.join(",", this.properties.getAdditionalHosts()));
|
||||
}
|
||||
if (this.properties.getMongoClientDatabase() != null || this.properties.getReplicaSetName() != null
|
||||
|| this.properties.getAuthenticationDatabase() != null) {
|
||||
builder.append("/");
|
||||
if (this.properties.getMongoClientDatabase() != null) {
|
||||
builder.append(this.properties.getMongoClientDatabase());
|
||||
}
|
||||
else if (this.properties.getAuthenticationDatabase() != null) {
|
||||
builder.append(this.properties.getAuthenticationDatabase());
|
||||
}
|
||||
if (this.properties.getReplicaSetName() != null) {
|
||||
builder.append("?");
|
||||
builder.append("repliceSet=");
|
||||
builder.append(this.properties.getReplicaSetName());
|
||||
}
|
||||
}
|
||||
return new ConnectionString(builder.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public GridFs getGridFs() {
|
||||
return GridFs.of(PropertiesMongoConnectionDetails.this.properties.getGridfs().getDatabase(),
|
||||
PropertiesMongoConnectionDetails.this.properties.getGridfs().getBucket());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.mongo;
|
||||
|
||||
import com.mongodb.ConnectionString;
|
||||
import com.mongodb.MongoClientSettings;
|
||||
import org.bson.UuidRepresentation;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* A {@link MongoClientSettingsBuilderCustomizer} that applies standard settings to a
|
||||
* {@link MongoClientSettings}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class StandardMongoClientSettingsBuilderCustomizer implements MongoClientSettingsBuilderCustomizer, Ordered {
|
||||
|
||||
private final ConnectionString connectionString;
|
||||
|
||||
private final UuidRepresentation uuidRepresentation;
|
||||
|
||||
private int order = 0;
|
||||
|
||||
public StandardMongoClientSettingsBuilderCustomizer(ConnectionString connectionString,
|
||||
UuidRepresentation uuidRepresentation) {
|
||||
this.connectionString = connectionString;
|
||||
this.uuidRepresentation = uuidRepresentation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(MongoClientSettings.Builder settingsBuilder) {
|
||||
settingsBuilder.uuidRepresentation(this.uuidRepresentation);
|
||||
settingsBuilder.applyConnectionString(this.connectionString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the order value of this object.
|
||||
* @param order the new order value
|
||||
* @see #getOrder()
|
||||
*/
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,12 +36,14 @@ import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
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.neo4j.Neo4jProperties.Authentication;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jProperties.Pool;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jProperties.Security;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -49,6 +51,9 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 2.4.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@@ -56,50 +61,24 @@ import org.springframework.util.StringUtils;
|
||||
@EnableConfigurationProperties(Neo4jProperties.class)
|
||||
public class Neo4jAutoConfiguration {
|
||||
|
||||
private static final URI DEFAULT_SERVER_URI = URI.create("bolt://localhost:7687");
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Driver neo4jDriver(Neo4jProperties properties, Environment environment,
|
||||
ObjectProvider<ConfigBuilderCustomizer> configBuilderCustomizers) {
|
||||
AuthToken authToken = mapAuthToken(properties.getAuthentication(), environment);
|
||||
Config config = mapDriverConfig(properties, configBuilderCustomizers.orderedStream().toList());
|
||||
URI serverUri = determineServerUri(properties, environment);
|
||||
return GraphDatabase.driver(serverUri, authToken, config);
|
||||
ObjectProvider<ConfigBuilderCustomizer> configBuilderCustomizers,
|
||||
ObjectProvider<Neo4jConnectionDetails> connectionDetailsProvider) {
|
||||
Neo4jConnectionDetails connectionDetails = connectionDetailsProvider
|
||||
.getIfAvailable(() -> new PropertiesNeo4jConnectionDetails(properties));
|
||||
AuthToken authToken = connectionDetails.getAuthToken();
|
||||
Config config = mapDriverConfig(properties, connectionDetails,
|
||||
configBuilderCustomizers.orderedStream().toList());
|
||||
return GraphDatabase.driver(connectionDetails.getUri(), authToken, config);
|
||||
}
|
||||
|
||||
URI determineServerUri(Neo4jProperties properties, Environment environment) {
|
||||
URI uri = properties.getUri();
|
||||
return (uri != null) ? uri : DEFAULT_SERVER_URI;
|
||||
}
|
||||
|
||||
AuthToken mapAuthToken(Neo4jProperties.Authentication authentication, Environment environment) {
|
||||
String username = authentication.getUsername();
|
||||
String password = authentication.getPassword();
|
||||
String kerberosTicket = authentication.getKerberosTicket();
|
||||
String realm = authentication.getRealm();
|
||||
|
||||
boolean hasUsername = StringUtils.hasText(username);
|
||||
boolean hasPassword = StringUtils.hasText(password);
|
||||
boolean hasKerberosTicket = StringUtils.hasText(kerberosTicket);
|
||||
|
||||
if (hasUsername && hasKerberosTicket) {
|
||||
throw new IllegalStateException(String
|
||||
.format("Cannot specify both username ('%s') and kerberos ticket ('%s')", username, kerberosTicket));
|
||||
}
|
||||
if (hasUsername && hasPassword) {
|
||||
return AuthTokens.basic(username, password, realm);
|
||||
}
|
||||
if (hasKerberosTicket) {
|
||||
return AuthTokens.kerberos(kerberosTicket);
|
||||
}
|
||||
return AuthTokens.none();
|
||||
}
|
||||
|
||||
Config mapDriverConfig(Neo4jProperties properties, List<ConfigBuilderCustomizer> customizers) {
|
||||
Config mapDriverConfig(Neo4jProperties properties, Neo4jConnectionDetails connectionDetails,
|
||||
List<ConfigBuilderCustomizer> customizers) {
|
||||
Config.ConfigBuilder builder = Config.builder();
|
||||
configurePoolSettings(builder, properties.getPool());
|
||||
URI uri = properties.getUri();
|
||||
URI uri = connectionDetails.getUri();
|
||||
String scheme = (uri != null) ? uri.getScheme() : "bolt";
|
||||
configureDriverSettings(builder, properties, isSimpleScheme(scheme));
|
||||
builder.withLogging(new Neo4jSpringJclLogging());
|
||||
@@ -191,4 +170,43 @@ public class Neo4jAutoConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link Neo4jProperties} to {@link Neo4jConnectionDetails}.
|
||||
*/
|
||||
static class PropertiesNeo4jConnectionDetails implements Neo4jConnectionDetails {
|
||||
|
||||
private final Neo4jProperties properties;
|
||||
|
||||
PropertiesNeo4jConnectionDetails(Neo4jProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
URI uri = this.properties.getUri();
|
||||
return (uri != null) ? uri : Neo4jConnectionDetails.super.getUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthToken getAuthToken() {
|
||||
Authentication authentication = this.properties.getAuthentication();
|
||||
String username = authentication.getUsername();
|
||||
String kerberosTicket = authentication.getKerberosTicket();
|
||||
boolean hasUsername = StringUtils.hasText(username);
|
||||
boolean hasKerberosTicket = StringUtils.hasText(kerberosTicket);
|
||||
Assert.state(!(hasUsername && hasKerberosTicket),
|
||||
() -> "Cannot specify both username ('%s') and kerberos ticket ('%s')".formatted(username,
|
||||
kerberosTicket));
|
||||
String password = authentication.getPassword();
|
||||
if (hasUsername && StringUtils.hasText(password)) {
|
||||
return AuthTokens.basic(username, password, authentication.getRealm());
|
||||
}
|
||||
if (hasKerberosTicket) {
|
||||
return AuthTokens.kerberos(kerberosTicket);
|
||||
}
|
||||
return AuthTokens.none();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.neo4j;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.neo4j.driver.AuthToken;
|
||||
import org.neo4j.driver.AuthTokens;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to a Neo4j service.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface Neo4jConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* Returns the URI of the Neo4j server. Defaults to {@code bolt://localhost:7687"}.
|
||||
* @return the Neo4j server URI
|
||||
*/
|
||||
default URI getUri() {
|
||||
return URI.create("bolt://localhost:7687");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token to use for authentication. Defaults to {@link AuthTokens#none()}.
|
||||
* @return the auth token
|
||||
*/
|
||||
default AuthToken getAuthToken() {
|
||||
return AuthTokens.none();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,7 +53,7 @@ class ConnectionFactoryBeanCreationFailureAnalyzer
|
||||
private String getDescription(ConnectionFactoryBeanCreationException cause) {
|
||||
StringBuilder description = new StringBuilder();
|
||||
description.append("Failed to configure a ConnectionFactory: ");
|
||||
if (!StringUtils.hasText(cause.getProperties().getUrl())) {
|
||||
if (!StringUtils.hasText(cause.getUrl())) {
|
||||
description.append("'url' attribute is not specified and ");
|
||||
}
|
||||
description.append(String.format("no embedded database could be configured.%n"));
|
||||
|
||||
@@ -51,14 +51,18 @@ import org.springframework.util.StringUtils;
|
||||
* @author Mark Paluch
|
||||
* @author Stephane Nicoll
|
||||
* @author Rodolpho S. Couto
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class ConnectionFactoryConfigurations {
|
||||
|
||||
protected static ConnectionFactory createConnectionFactory(R2dbcProperties properties, ClassLoader classLoader,
|
||||
protected static ConnectionFactory createConnectionFactory(R2dbcProperties properties,
|
||||
R2dbcConnectionDetails connectionDetails, ClassLoader classLoader,
|
||||
List<ConnectionFactoryOptionsBuilderCustomizer> optionsCustomizers) {
|
||||
try {
|
||||
return org.springframework.boot.r2dbc.ConnectionFactoryBuilder
|
||||
.withOptions(new ConnectionFactoryOptionsInitializer().initialize(properties,
|
||||
.withOptions(new ConnectionFactoryOptionsInitializer().initialize(properties, connectionDetails,
|
||||
() -> EmbeddedDatabaseConnection.get(classLoader)))
|
||||
.configure((options) -> {
|
||||
for (ConnectionFactoryOptionsBuilderCustomizer optionsCustomizer : optionsCustomizers) {
|
||||
@@ -87,10 +91,12 @@ abstract class ConnectionFactoryConfigurations {
|
||||
static class PooledConnectionFactoryConfiguration {
|
||||
|
||||
@Bean(destroyMethod = "dispose")
|
||||
ConnectionPool connectionFactory(R2dbcProperties properties, ResourceLoader resourceLoader,
|
||||
ConnectionPool connectionFactory(R2dbcProperties properties,
|
||||
ObjectProvider<R2dbcConnectionDetails> connectionDetails, ResourceLoader resourceLoader,
|
||||
ObjectProvider<ConnectionFactoryOptionsBuilderCustomizer> customizers) {
|
||||
ConnectionFactory connectionFactory = createConnectionFactory(properties,
|
||||
resourceLoader.getClassLoader(), customizers.orderedStream().toList());
|
||||
connectionDetails.getIfAvailable(), resourceLoader.getClassLoader(),
|
||||
customizers.orderedStream().toList());
|
||||
R2dbcProperties.Pool pool = properties.getPool();
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
ConnectionPoolConfiguration.Builder builder = ConnectionPoolConfiguration.builder(connectionFactory);
|
||||
@@ -116,10 +122,11 @@ abstract class ConnectionFactoryConfigurations {
|
||||
static class GenericConfiguration {
|
||||
|
||||
@Bean
|
||||
ConnectionFactory connectionFactory(R2dbcProperties properties, ResourceLoader resourceLoader,
|
||||
ConnectionFactory connectionFactory(R2dbcProperties properties,
|
||||
ObjectProvider<R2dbcConnectionDetails> connectionDetails, ResourceLoader resourceLoader,
|
||||
ObjectProvider<ConnectionFactoryOptionsBuilderCustomizer> customizers) {
|
||||
return createConnectionFactory(properties, resourceLoader.getClassLoader(),
|
||||
customizers.orderedStream().toList());
|
||||
return createConnectionFactory(properties, connectionDetails.getIfAvailable(),
|
||||
resourceLoader.getClassLoader(), customizers.orderedStream().toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,12 +16,10 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.r2dbc;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactoryOptions;
|
||||
import io.r2dbc.spi.ConnectionFactoryOptions.Builder;
|
||||
import io.r2dbc.spi.Option;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.boot.r2dbc.EmbeddedDatabaseConnection;
|
||||
@@ -31,6 +29,9 @@ import org.springframework.util.StringUtils;
|
||||
* Initialize a {@link Builder} based on {@link R2dbcProperties}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ConnectionFactoryOptionsInitializer {
|
||||
|
||||
@@ -38,45 +39,31 @@ class ConnectionFactoryOptionsInitializer {
|
||||
* Initialize a {@link Builder ConnectionFactoryOptions.Builder} using the specified
|
||||
* properties.
|
||||
* @param properties the properties to use to initialize the builder
|
||||
* @param connectionDetails the connection details to use to initialize the builder
|
||||
* @param embeddedDatabaseConnection the embedded connection to use as a fallback
|
||||
* @return an initialized builder
|
||||
* @throws ConnectionFactoryBeanCreationException if no suitable connection could be
|
||||
* determined
|
||||
*/
|
||||
ConnectionFactoryOptions.Builder initialize(R2dbcProperties properties,
|
||||
ConnectionFactoryOptions.Builder initialize(R2dbcProperties properties, R2dbcConnectionDetails connectionDetails,
|
||||
Supplier<EmbeddedDatabaseConnection> embeddedDatabaseConnection) {
|
||||
if (StringUtils.hasText(properties.getUrl())) {
|
||||
return initializeRegularOptions(properties);
|
||||
if (connectionDetails != null) {
|
||||
return connectionDetails.getConnectionFactoryOptions().mutate();
|
||||
}
|
||||
EmbeddedDatabaseConnection embeddedConnection = embeddedDatabaseConnection.get();
|
||||
if (embeddedConnection != EmbeddedDatabaseConnection.NONE) {
|
||||
return initializeEmbeddedOptions(properties, embeddedConnection);
|
||||
}
|
||||
throw connectionFactoryBeanCreationException("Failed to determine a suitable R2DBC Connection URL", properties,
|
||||
throw connectionFactoryBeanCreationException("Failed to determine a suitable R2DBC Connection URL", null,
|
||||
embeddedConnection);
|
||||
}
|
||||
|
||||
private ConnectionFactoryOptions.Builder initializeRegularOptions(R2dbcProperties properties) {
|
||||
ConnectionFactoryOptions urlOptions = ConnectionFactoryOptions.parse(properties.getUrl());
|
||||
Builder optionsBuilder = urlOptions.mutate();
|
||||
configureIf(optionsBuilder, urlOptions, ConnectionFactoryOptions.USER, properties::getUsername,
|
||||
StringUtils::hasText);
|
||||
configureIf(optionsBuilder, urlOptions, ConnectionFactoryOptions.PASSWORD, properties::getPassword,
|
||||
StringUtils::hasText);
|
||||
configureIf(optionsBuilder, urlOptions, ConnectionFactoryOptions.DATABASE,
|
||||
() -> determineDatabaseName(properties), StringUtils::hasText);
|
||||
if (properties.getProperties() != null) {
|
||||
properties.getProperties().forEach((key, value) -> optionsBuilder.option(Option.valueOf(key), value));
|
||||
}
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
private Builder initializeEmbeddedOptions(R2dbcProperties properties,
|
||||
EmbeddedDatabaseConnection embeddedDatabaseConnection) {
|
||||
String url = embeddedDatabaseConnection.getUrl(determineEmbeddedDatabaseName(properties));
|
||||
if (url == null) {
|
||||
throw connectionFactoryBeanCreationException("Failed to determine a suitable R2DBC Connection URL",
|
||||
properties, embeddedDatabaseConnection);
|
||||
throw connectionFactoryBeanCreationException("Failed to determine a suitable R2DBC Connection URL", url,
|
||||
embeddedDatabaseConnection);
|
||||
}
|
||||
Builder builder = ConnectionFactoryOptions.parse(url).mutate();
|
||||
String username = determineEmbeddedUsername(properties);
|
||||
@@ -89,6 +76,11 @@ class ConnectionFactoryOptionsInitializer {
|
||||
return builder;
|
||||
}
|
||||
|
||||
private String determineEmbeddedDatabaseName(R2dbcProperties properties) {
|
||||
String databaseName = determineDatabaseName(properties);
|
||||
return (databaseName != null) ? databaseName : "testdb";
|
||||
}
|
||||
|
||||
private String determineDatabaseName(R2dbcProperties properties) {
|
||||
if (properties.isGenerateUniqueName()) {
|
||||
return properties.determineUniqueName();
|
||||
@@ -99,30 +91,14 @@ class ConnectionFactoryOptionsInitializer {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String determineEmbeddedDatabaseName(R2dbcProperties properties) {
|
||||
String databaseName = determineDatabaseName(properties);
|
||||
return (databaseName != null) ? databaseName : "testdb";
|
||||
}
|
||||
|
||||
private String determineEmbeddedUsername(R2dbcProperties properties) {
|
||||
String username = ifHasText(properties.getUsername());
|
||||
return (username != null) ? username : "sa";
|
||||
}
|
||||
|
||||
private <T extends CharSequence> void configureIf(Builder optionsBuilder, ConnectionFactoryOptions originalOptions,
|
||||
Option<T> option, Supplier<T> valueSupplier, Predicate<T> setIf) {
|
||||
if (originalOptions.hasOption(option)) {
|
||||
return;
|
||||
}
|
||||
T value = valueSupplier.get();
|
||||
if (setIf.test(value)) {
|
||||
optionsBuilder.option(option, value);
|
||||
}
|
||||
}
|
||||
|
||||
private ConnectionFactoryBeanCreationException connectionFactoryBeanCreationException(String message,
|
||||
R2dbcProperties properties, EmbeddedDatabaseConnection embeddedDatabaseConnection) {
|
||||
return new ConnectionFactoryBeanCreationException(message, properties, embeddedDatabaseConnection);
|
||||
String r2dbcUrl, EmbeddedDatabaseConnection embeddedDatabaseConnection) {
|
||||
return new ConnectionFactoryBeanCreationException(message, r2dbcUrl, embeddedDatabaseConnection);
|
||||
}
|
||||
|
||||
private String ifHasText(String candidate) {
|
||||
@@ -131,25 +107,25 @@ class ConnectionFactoryOptionsInitializer {
|
||||
|
||||
static class ConnectionFactoryBeanCreationException extends BeanCreationException {
|
||||
|
||||
private final R2dbcProperties properties;
|
||||
private final String url;
|
||||
|
||||
private final EmbeddedDatabaseConnection embeddedDatabaseConnection;
|
||||
|
||||
ConnectionFactoryBeanCreationException(String message, R2dbcProperties properties,
|
||||
ConnectionFactoryBeanCreationException(String message, String url,
|
||||
EmbeddedDatabaseConnection embeddedDatabaseConnection) {
|
||||
super(message);
|
||||
this.properties = properties;
|
||||
this.url = url;
|
||||
this.embeddedDatabaseConnection = embeddedDatabaseConnection;
|
||||
}
|
||||
|
||||
String getUrl() {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
EmbeddedDatabaseConnection getEmbeddedDatabaseConnection() {
|
||||
return this.embeddedDatabaseConnection;
|
||||
}
|
||||
|
||||
R2dbcProperties getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,16 +16,26 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.r2dbc;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.ConnectionFactoryOptions;
|
||||
import io.r2dbc.spi.ConnectionFactoryOptions.Builder;
|
||||
import io.r2dbc.spi.Option;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
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.boot.autoconfigure.condition.ConditionalOnResource;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for R2DBC.
|
||||
@@ -42,4 +52,63 @@ import org.springframework.context.annotation.Import;
|
||||
ConnectionFactoryConfigurations.GenericConfiguration.class, ConnectionFactoryDependentConfiguration.class })
|
||||
public class R2dbcAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(R2dbcConnectionDetails.class)
|
||||
@ConditionalOnProperty("spring.r2dbc.url")
|
||||
PropertiesR2dbcConnectionDetails propertiesR2dbcConnectionDetails(R2dbcProperties properties) {
|
||||
return new PropertiesR2dbcConnectionDetails(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link R2dbcProperties} to {@link R2dbcConnectionDetails}.
|
||||
*/
|
||||
static class PropertiesR2dbcConnectionDetails implements R2dbcConnectionDetails {
|
||||
|
||||
private final R2dbcProperties properties;
|
||||
|
||||
PropertiesR2dbcConnectionDetails(R2dbcProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionFactoryOptions getConnectionFactoryOptions() {
|
||||
ConnectionFactoryOptions urlOptions = ConnectionFactoryOptions.parse(this.properties.getUrl());
|
||||
Builder optionsBuilder = urlOptions.mutate();
|
||||
configureIf(optionsBuilder, urlOptions, ConnectionFactoryOptions.USER, this.properties::getUsername,
|
||||
StringUtils::hasText);
|
||||
configureIf(optionsBuilder, urlOptions, ConnectionFactoryOptions.PASSWORD, this.properties::getPassword,
|
||||
StringUtils::hasText);
|
||||
configureIf(optionsBuilder, urlOptions, ConnectionFactoryOptions.DATABASE,
|
||||
() -> determineDatabaseName(this.properties), StringUtils::hasText);
|
||||
if (this.properties.getProperties() != null) {
|
||||
this.properties.getProperties()
|
||||
.forEach((key, value) -> optionsBuilder.option(Option.valueOf(key), value));
|
||||
}
|
||||
return optionsBuilder.build();
|
||||
}
|
||||
|
||||
private <T extends CharSequence> void configureIf(Builder optionsBuilder,
|
||||
ConnectionFactoryOptions originalOptions, Option<T> option, Supplier<T> valueSupplier,
|
||||
Predicate<T> setIf) {
|
||||
if (originalOptions.hasOption(option)) {
|
||||
return;
|
||||
}
|
||||
T value = valueSupplier.get();
|
||||
if (setIf.test(value)) {
|
||||
optionsBuilder.option(option, value);
|
||||
}
|
||||
}
|
||||
|
||||
private String determineDatabaseName(R2dbcProperties properties) {
|
||||
if (properties.isGenerateUniqueName()) {
|
||||
return properties.determineUniqueName();
|
||||
}
|
||||
if (StringUtils.hasLength(properties.getName())) {
|
||||
return properties.getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.r2dbc;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactoryOptions;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to an SQL service using R2DBC.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface R2dbcConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* Connection factory options for connecting to the database.
|
||||
* @return the connection factory options
|
||||
*/
|
||||
ConnectionFactoryOptions getConnectionFactoryOptions();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.service.connection;
|
||||
|
||||
import org.springframework.boot.origin.OriginProvider;
|
||||
|
||||
/**
|
||||
* Base interface for types that provide the details required to establish a connection to
|
||||
* a remote service.
|
||||
* <p>
|
||||
* Implementation classes can also implement {@link OriginProvider} in order to provide
|
||||
* origin information.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface ConnectionDetails {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Support for service connections that affect auto-configuration.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.service.connection;
|
||||
@@ -96,6 +96,9 @@ import static org.mockito.Mockito.mock;
|
||||
* @author Gary Russell
|
||||
* @author HaiTao Zhang
|
||||
* @author Franjo Zilic
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class RabbitAutoConfigurationTests {
|
||||
@@ -169,6 +172,26 @@ class RabbitAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void testConnectionFactoryWithOverridesWhenUsingConnectionDetails() {
|
||||
this.contextRunner.withUserConfiguration(TestConfiguration.class, ConnectionDetailsConfiguration.class)
|
||||
.withPropertyValues("spring.rabbitmq.host:remote-server", "spring.rabbitmq.port:9000",
|
||||
"spring.rabbitmq.username:alice", "spring.rabbitmq.password:secret",
|
||||
"spring.rabbitmq.virtual_host:/vhost")
|
||||
.run((context) -> {
|
||||
CachingConnectionFactory connectionFactory = context.getBean(CachingConnectionFactory.class);
|
||||
assertThat(connectionFactory.getHost()).isEqualTo("rabbit.example.com");
|
||||
assertThat(connectionFactory.getPort()).isEqualTo(12345);
|
||||
assertThat(connectionFactory.getVirtualHost()).isEqualTo("/vhost-1");
|
||||
assertThat(connectionFactory.getUsername()).isEqualTo("user-1");
|
||||
assertThat(connectionFactory.getRabbitConnectionFactory().getPassword()).isEqualTo("password-1");
|
||||
List<Address> addresses = (List<Address>) ReflectionTestUtils.getField(connectionFactory, "addresses");
|
||||
assertThat(addresses).containsExactly(new Address("rabbit.example.com", 12345),
|
||||
new Address("rabbit2.example.com", 23456));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void testConnectionFactoryWithCustomConnectionNameStrategy() {
|
||||
@@ -1218,6 +1241,38 @@ class RabbitAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
RabbitConnectionDetails rabbitConnectionDetails() {
|
||||
return new RabbitConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "password-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVirtualHost() {
|
||||
return "/vhost-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Address> getAddresses() {
|
||||
return List.of(new Address("rabbit.example.com", 12345), new Address("rabbit2.example.com", 23456));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestListener {
|
||||
|
||||
@RabbitListener(queues = "test", autoStartup = "false")
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.cassandra;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlIdentifier;
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
@@ -45,6 +46,9 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
* @author Eddú Meléndez
|
||||
* @author Stephane Nicoll
|
||||
* @author Ittay Stern
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CassandraAutoConfigurationTests {
|
||||
|
||||
@@ -90,6 +94,26 @@ class CassandraAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseConnectionDetails() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.cassandra.contact-points=localhost:9042", "spring.cassandra.username=a-user",
|
||||
"spring.cassandra.password=a-password", "spring.cassandra.local-datacenter=some-datacenter")
|
||||
.withBean(CassandraConnectionDetails.class, this::cassandraConnectionDetails)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(DriverConfigLoader.class);
|
||||
DriverExecutionProfile configuration = context.getBean(DriverConfigLoader.class)
|
||||
.getInitialConfig()
|
||||
.getDefaultProfile();
|
||||
assertThat(configuration.getStringList(DefaultDriverOption.CONTACT_POINTS))
|
||||
.containsOnly("cassandra.example.com:9042");
|
||||
assertThat(configuration.getString(DefaultDriverOption.AUTH_PROVIDER_USER_NAME)).isEqualTo("user-1");
|
||||
assertThat(configuration.getString(DefaultDriverOption.AUTH_PROVIDER_PASSWORD)).isEqualTo("secret-1");
|
||||
assertThat(configuration.getString(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER))
|
||||
.isEqualTo("datacenter-1");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void driverConfigLoaderWithContactPointAndNoPort() {
|
||||
this.contextRunner
|
||||
@@ -310,6 +334,32 @@ class CassandraAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
private CassandraConnectionDetails cassandraConnectionDetails() {
|
||||
return new CassandraConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public List<Node> getContactPoints() {
|
||||
return List.of(new Node("cassandra.example.com", 9042));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "secret-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocalDatacenter() {
|
||||
return "datacenter-1";
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class SimpleDriverConfigLoaderBuilderCustomizerConfig {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.data.mongo;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.mongodb.ConnectionString;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -31,6 +32,7 @@ import org.springframework.boot.autoconfigure.data.mongo.city.City;
|
||||
import org.springframework.boot.autoconfigure.data.mongo.country.Country;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -58,6 +60,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Josh Long
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class MongoDataAutoConfigurationTests {
|
||||
|
||||
@@ -80,6 +85,17 @@ class MongoDataAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesMongoConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(GridFsTemplate.class);
|
||||
GridFsTemplate template = context.getBean(GridFsTemplate.class);
|
||||
assertThat(template).hasFieldOrPropertyWithValue("bucket", "connection-details-bucket");
|
||||
MongoDatabaseFactory factory = (MongoDatabaseFactory) ReflectionTestUtils.getField(template, "dbFactory");
|
||||
assertThat(factory.getMongoDatabase().getName()).isEqualTo("grid-database-1");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenGridFsBucketIsConfiguredThenGridFsTemplateIsAutoConfiguredAndUsesIt() {
|
||||
this.contextRunner.withPropertyValues("spring.data.mongodb.gridfs.bucket:test-bucket").run((context) -> {
|
||||
@@ -250,6 +266,28 @@ class MongoDataAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
MongoConnectionDetails mongoConnectionDetails() {
|
||||
return new MongoConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public ConnectionString getConnectionString() {
|
||||
return new ConnectionString("mongodb://localhost/db");
|
||||
}
|
||||
|
||||
@Override
|
||||
public GridFs getGridFs() {
|
||||
return GridFs.of("grid-database-1", "connection-details-bucket");
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MyConverter implements Converter<MongoClient, Boolean> {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,13 +16,17 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.data.mongo;
|
||||
|
||||
import com.mongodb.ConnectionString;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
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.gridfs.ReactiveGridFsTemplate;
|
||||
@@ -35,6 +39,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Artsiom Yudovin
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class MongoReactiveDataAutoConfigurationTests {
|
||||
|
||||
@@ -58,6 +65,15 @@ class MongoReactiveDataAutoConfigurationTests {
|
||||
.run((context) -> assertThat(grisFsTemplateDatabaseName(context)).isEqualTo("grid"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesMongoConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class).run((context) -> {
|
||||
assertThat(grisFsTemplateDatabaseName(context)).isEqualTo("grid-database-1");
|
||||
ReactiveGridFsTemplate template = context.getBean(ReactiveGridFsTemplate.class);
|
||||
assertThat(template).hasFieldOrPropertyWithValue("bucket", "connection-details-bucket");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenGridFsBucketIsConfiguredThenGridFsTemplateUsesIt() {
|
||||
this.contextRunner.withPropertyValues("spring.data.mongodb.gridfs.bucket:test-bucket").run((context) -> {
|
||||
@@ -82,4 +98,38 @@ class MongoReactiveDataAutoConfigurationTests {
|
||||
return factory.getMongoDatabase().block().getName();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
MongoConnectionDetails mongoConnectionDetails() {
|
||||
return new MongoConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public ConnectionString getConnectionString() {
|
||||
return new ConnectionString("mongodb://localhost/db");
|
||||
}
|
||||
|
||||
@Override
|
||||
public GridFs getGridFs() {
|
||||
return new GridFs() {
|
||||
|
||||
@Override
|
||||
public String getDatabase() {
|
||||
return "grid-database-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBucket() {
|
||||
return "connection-details-bucket";
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,6 +39,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Mark Paluch
|
||||
* @author Stephane Nicoll
|
||||
* @author Weix Sun
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@ClassPathExclusions("lettuce-core-*.jar")
|
||||
class RedisAutoConfigurationJedisTests {
|
||||
@@ -79,6 +82,14 @@ class RedisAutoConfigurationJedisTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class).run((context) -> {
|
||||
JedisConnectionFactory cf = context.getBean(JedisConnectionFactory.class);
|
||||
assertThat(cf.isUseSsl()).isFalse();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRedisUrlConfiguration() {
|
||||
this.contextRunner
|
||||
@@ -240,6 +251,35 @@ class RedisAutoConfigurationJedisTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
RedisConnectionDetails redisConnectionDetails() {
|
||||
return new RedisConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public Standalone getStandalone() {
|
||||
return new Standalone() {
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return "localhost";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return 6379;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JedisConnectionFactoryCaptorConfiguration {
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisNode;
|
||||
import org.springframework.data.redis.connection.RedisPassword;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
@@ -71,6 +72,9 @@ import static org.mockito.Mockito.mock;
|
||||
* @author Alen Turkovic
|
||||
* @author Scott Frederick
|
||||
* @author Weix Sun
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class RedisAutoConfigurationTests {
|
||||
|
||||
@@ -490,6 +494,51 @@ class RedisAutoConfigurationTests {
|
||||
(options) -> assertThat(options.getTopologyRefreshOptions().useDynamicRefreshSources()).isTrue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesStandaloneFromConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsStandaloneConfiguration.class).run((context) -> {
|
||||
LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(cf.isUseSsl()).isFalse();
|
||||
RedisStandaloneConfiguration configuration = cf.getStandaloneConfiguration();
|
||||
assertThat(configuration.getHostName()).isEqualTo("redis.example.com");
|
||||
assertThat(configuration.getPort()).isEqualTo(16379);
|
||||
assertThat(configuration.getDatabase()).isOne();
|
||||
assertThat(configuration.getUsername()).isEqualTo("user-1");
|
||||
assertThat(configuration.getPassword()).isEqualTo(RedisPassword.of("password-1"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesSentinelFromConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsSentinelConfiguration.class).run((context) -> {
|
||||
LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(cf.isUseSsl()).isFalse();
|
||||
RedisSentinelConfiguration configuration = cf.getSentinelConfiguration();
|
||||
assertThat(configuration).isNotNull();
|
||||
assertThat(configuration.getSentinelUsername()).isEqualTo("sentinel-1");
|
||||
assertThat(configuration.getSentinelPassword().get()).isEqualTo("secret-1".toCharArray());
|
||||
assertThat(configuration.getSentinels()).containsExactly(new RedisNode("node-1", 12345));
|
||||
assertThat(configuration.getUsername()).isEqualTo("user-1");
|
||||
assertThat(configuration.getPassword()).isEqualTo(RedisPassword.of("password-1"));
|
||||
assertThat(configuration.getDatabase()).isOne();
|
||||
assertThat(configuration.getMaster().getName()).isEqualTo("master.redis.example.com");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesClusterFromConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsClusterConfiguration.class).run((context) -> {
|
||||
LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(cf.isUseSsl()).isFalse();
|
||||
RedisClusterConfiguration configuration = cf.getClusterConfiguration();
|
||||
assertThat(configuration).isNotNull();
|
||||
assertThat(configuration.getUsername()).isEqualTo("user-1");
|
||||
assertThat(configuration.getPassword().get()).isEqualTo("password-1".toCharArray());
|
||||
assertThat(configuration.getClusterNodes()).containsExactly(new RedisNode("node-1", 12345),
|
||||
new RedisNode("node-2", 23456));
|
||||
});
|
||||
}
|
||||
|
||||
private <T extends ClientOptions> ContextConsumer<AssertableApplicationContext> assertClientOptions(
|
||||
Class<T> expectedType, Consumer<T> options) {
|
||||
return (context) -> {
|
||||
@@ -532,4 +581,136 @@ class RedisAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsStandaloneConfiguration {
|
||||
|
||||
@Bean
|
||||
RedisConnectionDetails redisConnectionDetails() {
|
||||
return new RedisConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "password-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Standalone getStandalone() {
|
||||
return new Standalone() {
|
||||
|
||||
@Override
|
||||
public int getDatabase() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return "redis.example.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return 16379;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsSentinelConfiguration {
|
||||
|
||||
@Bean
|
||||
RedisConnectionDetails redisConnectionDetails() {
|
||||
return new RedisConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "password-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sentinel getSentinel() {
|
||||
return new Sentinel() {
|
||||
|
||||
@Override
|
||||
public int getDatabase() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMaster() {
|
||||
return "master.redis.example.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getNodes() {
|
||||
return List.of(new Node("node-1", 12345));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "sentinel-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "secret-1";
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsClusterConfiguration {
|
||||
|
||||
@Bean
|
||||
RedisConnectionDetails redisConnectionDetails() {
|
||||
return new RedisConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "password-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cluster getCluster() {
|
||||
return new Cluster() {
|
||||
|
||||
@Override
|
||||
public List<Node> getNodes() {
|
||||
return List.of(new Node("node-1", 12345), new Node("node-2", 23456));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.boot.autoconfigure.elasticsearch;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.auth.AuthScope;
|
||||
@@ -32,6 +33,7 @@ import org.elasticsearch.client.sniff.Sniffer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails.Node.Protocol;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -49,6 +51,8 @@ import static org.mockito.Mockito.mock;
|
||||
* @author Evgeniy Cheban
|
||||
* @author Filip Hrisafov
|
||||
* @author Andy Wilkinson
|
||||
* @author Moritz Halbritter
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ElasticsearchRestClientAutoConfigurationTests {
|
||||
|
||||
@@ -243,6 +247,65 @@ class ElasticsearchRestClientAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectionDetailsAreUsedIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(RestClient.class);
|
||||
RestClient restClient = context.getBean(RestClient.class);
|
||||
assertThat(restClient).hasFieldOrPropertyWithValue("pathPrefix", "/some-path");
|
||||
assertThat(restClient.getNodes().stream().map(Node::getHost).map(HttpHost::toString))
|
||||
.containsExactly("http://elastic.example.com:9200");
|
||||
assertThat(restClient)
|
||||
.extracting("client.credentialsProvider", InstanceOfAssertFactories.type(CredentialsProvider.class))
|
||||
.satisfies((credentialsProvider) -> {
|
||||
Credentials uriCredentials = credentialsProvider
|
||||
.getCredentials(new AuthScope("any.elastic.example.com", 80));
|
||||
assertThat(uriCredentials.getUserPrincipal().getName()).isEqualTo("user-1");
|
||||
assertThat(uriCredentials.getPassword()).isEqualTo("password-1");
|
||||
})
|
||||
.satisfies((credentialsProvider) -> {
|
||||
Credentials uriCredentials = credentialsProvider
|
||||
.getCredentials(new AuthScope("elastic.example.com", 9200));
|
||||
assertThat(uriCredentials.getUserPrincipal().getName()).isEqualTo("node-user-1");
|
||||
assertThat(uriCredentials.getPassword()).isEqualTo("node-password-1");
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
ElasticsearchConnectionDetails elasticsearchConnectionDetails() {
|
||||
return new ElasticsearchConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public List<Node> getNodes() {
|
||||
return List
|
||||
.of(new Node("elastic.example.com", 9200, Protocol.HTTP, "node-user-1", "node-password-1"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "password-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathPrefix() {
|
||||
return "/some-path";
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class BuilderCustomizerConfiguration {
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayAutoConfigurationRuntimeHints;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
|
||||
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.boot.jdbc.SchemaManagement;
|
||||
@@ -120,6 +121,16 @@ class FlywayAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsDataSourceWithNoDataSourceBeanAndJdbcConnectionDetails() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(JdbcConnectionDetailsConfiguration.class, MockFlywayMigrationStrategy.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(Flyway.class);
|
||||
assertThat(context.getBean(Flyway.class).getConfiguration().getDataSource()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void backsOffWithFlywayUrlAndNoSpringJdbc() {
|
||||
this.contextRunner.withPropertyValues("spring.flyway.url:jdbc:hsqldb:mem:" + UUID.randomUUID())
|
||||
@@ -137,6 +148,28 @@ class FlywayAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDataSourceWithJdbcConnectionDetails() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(EmbeddedDataSourceConfiguration.class, JdbcConnectionDetailsConfiguration.class,
|
||||
MockFlywayMigrationStrategy.class)
|
||||
.withPropertyValues("spring.flyway.url=jdbc:hsqldb:mem:flywaytest", "spring.flyway.user=some-user",
|
||||
"spring.flyway.password=some-password",
|
||||
"spring.flyway.driver-class-name=org.hsqldb.jdbc.JDBCDriver")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(Flyway.class);
|
||||
Flyway flyway = context.getBean(Flyway.class);
|
||||
DataSource dataSource = flyway.getConfiguration().getDataSource();
|
||||
assertThat(dataSource).isInstanceOf(SimpleDriverDataSource.class);
|
||||
SimpleDriverDataSource simpleDriverDataSource = (SimpleDriverDataSource) dataSource;
|
||||
assertThat(simpleDriverDataSource.getUrl())
|
||||
.isEqualTo("jdbc:postgresql://database.example.com:12345/database-1");
|
||||
assertThat(simpleDriverDataSource.getUsername()).isEqualTo("user-1");
|
||||
assertThat(simpleDriverDataSource.getPassword()).isEqualTo("secret-1");
|
||||
assertThat(simpleDriverDataSource.getDriver()).isInstanceOf(org.postgresql.Driver.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDataSourceWithUser() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
@@ -200,6 +233,19 @@ class FlywayAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void flywayDataSourceIsUsedWhenJdbcConnectionDetailsIsAvailable() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(FlywayDataSourceConfiguration.class, EmbeddedDataSourceConfiguration.class,
|
||||
JdbcConnectionDetailsConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JdbcConnectionDetails.class);
|
||||
assertThat(context).hasSingleBean(Flyway.class);
|
||||
assertThat(context.getBean(Flyway.class).getConfiguration().getDataSource())
|
||||
.isEqualTo(context.getBean("flywayDataSource"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void flywayDataSourceWithoutDataSourceAutoConfiguration() {
|
||||
this.contextRunner.withUserConfiguration(FlywayDataSourceConfiguration.class).run((context) -> {
|
||||
@@ -1036,4 +1082,31 @@ class FlywayAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JdbcConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails jdbcConnectionDetails() {
|
||||
return new JdbcConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return "jdbc:postgresql://database.example.com:12345/database-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "secret-1";
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.influx;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
@@ -38,6 +39,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Sergey Kuptsov
|
||||
* @author Stephane Nicoll
|
||||
* @author Eddú Meléndez
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class InfluxDbAutoConfigurationTests {
|
||||
|
||||
@@ -49,6 +53,27 @@ class InfluxDbAutoConfigurationTests {
|
||||
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(InfluxDB.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseConnectionDetails() {
|
||||
this.contextRunner.withBean(InfluxDbConnectionDetails.class, this::influxDbConnectionDetails).run((context) -> {
|
||||
assertThat(context).hasSingleBean(InfluxDB.class);
|
||||
InfluxDB influxDb = context.getBean(InfluxDB.class);
|
||||
assertThat(influxDb).hasFieldOrPropertyWithValue("hostName", "localhost");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectionDetailsOverwriteProperties() {
|
||||
this.contextRunner.withBean(InfluxDbConnectionDetails.class, this::influxDbConnectionDetails)
|
||||
.withPropertyValues("spring.influx.url=http://some-other-host", "spring.influx.user=user",
|
||||
"spring.influx.password=password")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(InfluxDB.class);
|
||||
InfluxDB influxDb = context.getBean(InfluxDB.class);
|
||||
assertThat(influxDb).hasFieldOrPropertyWithValue("hostName", "localhost");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void influxDbCanBeCustomized() {
|
||||
this.contextRunner
|
||||
@@ -95,6 +120,27 @@ class InfluxDbAutoConfigurationTests {
|
||||
return callFactory.readTimeoutMillis();
|
||||
}
|
||||
|
||||
private InfluxDbConnectionDetails influxDbConnectionDetails() {
|
||||
return new InfluxDbConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public URI getUrl() {
|
||||
return URI.create("http://localhost");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "password-1";
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomOkHttpClientBuilderProviderConfig {
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.zaxxer.hikari.HikariDataSource;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import oracle.ucp.jdbc.PoolDataSourceImpl;
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
@@ -60,6 +61,9 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class DataSourceAutoConfigurationTests {
|
||||
|
||||
@@ -244,6 +248,41 @@ class DataSourceAutoConfigurationTests {
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(DataSourceScriptDatabaseInitializer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void dbcp2UsesJdbcConnectionDetailsIfAvailable() {
|
||||
ApplicationContextRunner runner = new ApplicationContextRunner()
|
||||
.withPropertyValues("spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource",
|
||||
"spring.datasource.dbcp2.url=jdbc:broken", "spring.datasource.dbcp2.username=alice",
|
||||
"spring.datasource.dbcp2.password=secret")
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class));
|
||||
runner.withUserConfiguration(JdbcConnectionDetailsConfiguration.class).run((context) -> {
|
||||
DataSource dataSource = context.getBean(DataSource.class);
|
||||
assertThat(dataSource).asInstanceOf(InstanceOfAssertFactories.type(BasicDataSource.class))
|
||||
.satisfies((dbcp2) -> {
|
||||
assertThat(dbcp2.getUsername()).isEqualTo("user-1");
|
||||
assertThat(dbcp2.getPassword()).isEqualTo("password-1");
|
||||
assertThat(dbcp2.getDriverClassName()).isEqualTo("org.postgresql.Driver");
|
||||
assertThat(dbcp2.getUrl()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void genericUsesJdbcConnectionDetailsIfAvailable() {
|
||||
ApplicationContextRunner runner = new ApplicationContextRunner()
|
||||
.withPropertyValues("spring.datasource.type=" + TestDataSource.class.getName())
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class));
|
||||
runner.withUserConfiguration(JdbcConnectionDetailsConfiguration.class).run((context) -> {
|
||||
DataSource dataSource = context.getBean(DataSource.class);
|
||||
assertThat(dataSource).isInstanceOf(TestDataSource.class);
|
||||
TestDataSource source = (TestDataSource) dataSource;
|
||||
assertThat(source.getUsername()).isEqualTo("user-1");
|
||||
assertThat(source.getPassword()).isEqualTo("password-1");
|
||||
assertThat(source.getDriver().getClass().getName()).isEqualTo("org.postgresql.Driver");
|
||||
assertThat(source.getUrl()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
});
|
||||
}
|
||||
|
||||
private static Function<ApplicationContextRunner, ApplicationContextRunner> hideConnectionPools() {
|
||||
return (runner) -> runner.withClassLoader(new FilteredClassLoader("org.apache.tomcat", "com.zaxxer.hikari",
|
||||
"org.apache.commons.dbcp2", "oracle.ucp.jdbc", "com.mchange"));
|
||||
@@ -259,6 +298,16 @@ class DataSourceAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JdbcConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails sqlJdbcConnectionDetails() {
|
||||
return new TestJdbcConnectionDetails();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class TestDataSourceConfiguration {
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link Dbcp2JdbcConnectionDetailsBeanPostProcessor}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class Dbcp2JdbcConnectionDetailsBeanPostProcessorTests {
|
||||
|
||||
@Test
|
||||
void setUsernamePasswordAndUrl() {
|
||||
BasicDataSource dataSource = new BasicDataSource();
|
||||
dataSource.setUrl("will-be-overwritten");
|
||||
dataSource.setUsername("will-be-overwritten");
|
||||
dataSource.setPassword("will-be-overwritten");
|
||||
dataSource.setDriverClassName("will-be-overwritten");
|
||||
new Dbcp2JdbcConnectionDetailsBeanPostProcessor(null).processDataSource(dataSource,
|
||||
new TestJdbcConnectionDetails());
|
||||
assertThat(dataSource.getUrl()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUsername()).isEqualTo("user-1");
|
||||
assertThat(dataSource.getPassword()).isEqualTo("password-1");
|
||||
assertThat(dataSource.getDriverClassName()).isEqualTo(DatabaseDriver.POSTGRESQL.getDriverClassName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,10 +19,13 @@ package org.springframework.boot.autoconfigure.jdbc;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -31,9 +34,14 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class HikariDataSourceConfigurationTests {
|
||||
|
||||
private static final String PREFIX = "spring.datasource.hikari.";
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
|
||||
.withPropertyValues("spring.datasource.type=" + HikariDataSource.class.getName());
|
||||
@@ -49,8 +57,7 @@ class HikariDataSourceConfigurationTests {
|
||||
@Test
|
||||
void testDataSourcePropertiesOverridden() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.datasource.hikari.jdbc-url=jdbc:foo//bar/spam",
|
||||
"spring.datasource.hikari.max-lifetime=1234")
|
||||
.withPropertyValues(PREFIX + "jdbc-url=jdbc:foo//bar/spam", "spring.datasource.hikari.max-lifetime=1234")
|
||||
.run((context) -> {
|
||||
HikariDataSource ds = context.getBean(HikariDataSource.class);
|
||||
assertThat(ds.getJdbcUrl()).isEqualTo("jdbc:foo//bar/spam");
|
||||
@@ -61,8 +68,7 @@ class HikariDataSourceConfigurationTests {
|
||||
@Test
|
||||
void testDataSourceGenericPropertiesOverridden() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.datasource.hikari.data-source-properties.dataSourceClassName=org.h2.JDBCDataSource")
|
||||
.withPropertyValues(PREFIX + "data-source-properties.dataSourceClassName=org.h2.JDBCDataSource")
|
||||
.run((context) -> {
|
||||
HikariDataSource ds = context.getBean(HikariDataSource.class);
|
||||
assertThat(ds.getDataSourceProperties().getProperty("dataSourceClassName"))
|
||||
@@ -90,12 +96,38 @@ class HikariDataSourceConfigurationTests {
|
||||
|
||||
@Test
|
||||
void poolNameTakesPrecedenceOverName() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.datasource.name=myDS", "spring.datasource.hikari.pool-name=myHikariDS")
|
||||
this.contextRunner.withPropertyValues("spring.datasource.name=myDS", PREFIX + "pool-name=myHikariDS")
|
||||
.run((context) -> {
|
||||
HikariDataSource ds = context.getBean(HikariDataSource.class);
|
||||
assertThat(ds.getPoolName()).isEqualTo("myHikariDS");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class)
|
||||
.withPropertyValues(PREFIX + "url=jdbc:broken", PREFIX + "username=alice", PREFIX + "password=secret")
|
||||
.run((context) -> {
|
||||
DataSource dataSource = context.getBean(DataSource.class);
|
||||
assertThat(dataSource).asInstanceOf(InstanceOfAssertFactories.type(HikariDataSource.class))
|
||||
.satisfies((hikari) -> {
|
||||
assertThat(hikari.getUsername()).isEqualTo("user-1");
|
||||
assertThat(hikari.getPassword()).isEqualTo("password-1");
|
||||
assertThat(hikari.getDriverClassName()).isEqualTo("org.postgresql.Driver");
|
||||
assertThat(hikari.getJdbcUrl())
|
||||
.isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails sqlConnectionDetails() {
|
||||
return new TestJdbcConnectionDetails();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link HikariJdbcConnectionDetailsBeanPostProcessor}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class HikariJdbcConnectionDetailsBeanPostProcessorTests {
|
||||
|
||||
@Test
|
||||
void setUsernamePasswordAndUrl() {
|
||||
HikariDataSource dataSource = new HikariDataSource();
|
||||
dataSource.setJdbcUrl("will-be-overwritten");
|
||||
dataSource.setUsername("will-be-overwritten");
|
||||
dataSource.setPassword("will-be-overwritten");
|
||||
dataSource.setDriverClassName(DatabaseDriver.H2.getDriverClassName());
|
||||
new HikariJdbcConnectionDetailsBeanPostProcessor(null).processDataSource(dataSource,
|
||||
new TestJdbcConnectionDetails());
|
||||
assertThat(dataSource.getJdbcUrl()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUsername()).isEqualTo("user-1");
|
||||
assertThat(dataSource.getPassword()).isEqualTo("password-1");
|
||||
assertThat(dataSource.getDriverClassName()).isEqualTo(DatabaseDriver.POSTGRESQL.getDriverClassName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,10 +22,13 @@ import javax.sql.DataSource;
|
||||
|
||||
import oracle.ucp.jdbc.PoolDataSource;
|
||||
import oracle.ucp.jdbc.PoolDataSourceImpl;
|
||||
import oracle.ucp.util.OpaqueString;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -34,9 +37,14 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Fabio Grassi
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class OracleUcpDataSourceConfigurationTests {
|
||||
|
||||
private static final String PREFIX = "spring.datasource.oracleucp.";
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
|
||||
.withPropertyValues("spring.datasource.type=" + PoolDataSource.class.getName());
|
||||
@@ -54,9 +62,7 @@ class OracleUcpDataSourceConfigurationTests {
|
||||
|
||||
@Test
|
||||
void testDataSourcePropertiesOverridden() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.datasource.oracleucp.url=jdbc:foo//bar/spam",
|
||||
"spring.datasource.oracleucp.max-idle-time=1234")
|
||||
this.contextRunner.withPropertyValues(PREFIX + "url=jdbc:foo//bar/spam", PREFIX + "max-idle-time=1234")
|
||||
.run((context) -> {
|
||||
PoolDataSourceImpl ds = context.getBean(PoolDataSourceImpl.class);
|
||||
assertThat(ds.getURL()).isEqualTo("jdbc:foo//bar/spam");
|
||||
@@ -66,11 +72,10 @@ class OracleUcpDataSourceConfigurationTests {
|
||||
|
||||
@Test
|
||||
void testDataSourceConnectionPropertiesOverridden() {
|
||||
this.contextRunner.withPropertyValues("spring.datasource.oracleucp.connection-properties.autoCommit=false")
|
||||
.run((context) -> {
|
||||
PoolDataSourceImpl ds = context.getBean(PoolDataSourceImpl.class);
|
||||
assertThat(ds.getConnectionProperty("autoCommit")).isEqualTo("false");
|
||||
});
|
||||
this.contextRunner.withPropertyValues(PREFIX + "connection-properties.autoCommit=false").run((context) -> {
|
||||
PoolDataSourceImpl ds = context.getBean(PoolDataSourceImpl.class);
|
||||
assertThat(ds.getConnectionProperty("autoCommit")).isEqualTo("false");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -100,12 +105,38 @@ class OracleUcpDataSourceConfigurationTests {
|
||||
@Test
|
||||
void poolNameTakesPrecedenceOverName() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.datasource.name=myDS",
|
||||
"spring.datasource.oracleucp.connection-pool-name=myOracleUcpDS")
|
||||
.withPropertyValues("spring.datasource.name=myDS", PREFIX + "connection-pool-name=myOracleUcpDS")
|
||||
.run((context) -> {
|
||||
PoolDataSourceImpl ds = context.getBean(PoolDataSourceImpl.class);
|
||||
assertThat(ds.getConnectionPoolName()).isEqualTo("myOracleUcpDS");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesJdbcConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class)
|
||||
.withPropertyValues(PREFIX + "url=jdbc:broken", PREFIX + "username=alice", PREFIX + "password=secret")
|
||||
.run((context) -> {
|
||||
DataSource dataSource = context.getBean(DataSource.class);
|
||||
assertThat(dataSource).isInstanceOf(PoolDataSourceImpl.class);
|
||||
PoolDataSourceImpl oracleUcp = (PoolDataSourceImpl) dataSource;
|
||||
assertThat(oracleUcp.getUser()).isEqualTo("user-1");
|
||||
assertThat(oracleUcp).extracting("password")
|
||||
.extracting((o) -> ((OpaqueString) o).get())
|
||||
.isEqualTo("password-1");
|
||||
assertThat(oracleUcp.getConnectionFactoryClassName()).isEqualTo("org.postgresql.Driver");
|
||||
assertThat(oracleUcp.getURL()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails jdbcConnectionDetails() {
|
||||
return new TestJdbcConnectionDetails();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import oracle.ucp.jdbc.PoolDataSourceImpl;
|
||||
import oracle.ucp.util.OpaqueString;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link OracleUcpJdbcConnectionDetailsBeanPostProcessor}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class OracleUcpJdbcConnectionDetailsBeanPostProcessorTests {
|
||||
|
||||
@Test
|
||||
void setUsernamePasswordAndUrl() throws SQLException {
|
||||
PoolDataSourceImpl dataSource = new PoolDataSourceImpl();
|
||||
dataSource.setURL("will-be-overwritten");
|
||||
dataSource.setUser("will-be-overwritten");
|
||||
dataSource.setPassword("will-be-overwritten");
|
||||
dataSource.setConnectionFactoryClassName("will-be-overwritten");
|
||||
new OracleUcpJdbcConnectionDetailsBeanPostProcessor(null).processDataSource(dataSource,
|
||||
new TestJdbcConnectionDetails());
|
||||
assertThat(dataSource.getURL()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUser()).isEqualTo("user-1");
|
||||
assertThat(dataSource).extracting("password")
|
||||
.extracting((password) -> ((OpaqueString) password).get())
|
||||
.isEqualTo("password-1");
|
||||
assertThat(dataSource.getConnectionFactoryClassName())
|
||||
.isEqualTo(DatabaseDriver.POSTGRESQL.getDriverClassName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
/**
|
||||
* {@link JdbcConnectionDetails} used in tests.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class TestJdbcConnectionDetails implements JdbcConnectionDetails {
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return "jdbc:postgresql://postgres.example.com:12345/database-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "password-1";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,9 +24,11 @@ import org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -41,6 +43,9 @@ import static org.junit.jupiter.api.Assertions.fail;
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class TomcatDataSourceConfigurationTests {
|
||||
|
||||
@@ -48,6 +53,10 @@ class TomcatDataSourceConfigurationTests {
|
||||
|
||||
private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
|
||||
.withPropertyValues("spring.datasource.type=" + org.apache.tomcat.jdbc.pool.DataSource.class.getName());
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
TestPropertyValues.of(PREFIX + "initialize:false").applyTo(this.context);
|
||||
@@ -106,6 +115,32 @@ class TomcatDataSourceConfigurationTests {
|
||||
assertThat(ds.getValidationInterval()).isEqualTo(3000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesJdbcConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class)
|
||||
.withPropertyValues(PREFIX + "url=jdbc:broken", PREFIX + "username=alice", PREFIX + "password=secret")
|
||||
.run((context) -> {
|
||||
DataSource dataSource = context.getBean(DataSource.class);
|
||||
assertThat(dataSource).isInstanceOf(org.apache.tomcat.jdbc.pool.DataSource.class);
|
||||
org.apache.tomcat.jdbc.pool.DataSource tomcat = (org.apache.tomcat.jdbc.pool.DataSource) dataSource;
|
||||
assertThat(tomcat.getPoolProperties().getUsername()).isEqualTo("user-1");
|
||||
assertThat(tomcat.getPoolProperties().getPassword()).isEqualTo("password-1");
|
||||
assertThat(tomcat.getPoolProperties().getDriverClassName()).isEqualTo("org.postgresql.Driver");
|
||||
assertThat(tomcat.getPoolProperties().getUrl())
|
||||
.isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails jdbcConnectionDetails() {
|
||||
return new TestJdbcConnectionDetails();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties
|
||||
@EnableMBeanExport
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import org.apache.tomcat.jdbc.pool.DataSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link TomcatJdbcConnectionDetailsBeanPostProcessor}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class TomcatJdbcConnectionDetailsBeanPostProcessorTests {
|
||||
|
||||
@Test
|
||||
void setUsernamePasswordAndUrl() {
|
||||
DataSource dataSource = new DataSource();
|
||||
dataSource.setUrl("will-be-overwritten");
|
||||
dataSource.setUsername("will-be-overwritten");
|
||||
dataSource.setPassword("will-be-overwritten");
|
||||
dataSource.setDriverClassName("will-be-overwritten");
|
||||
new TomcatJdbcConnectionDetailsBeanPostProcessor(null).processDataSource(dataSource,
|
||||
new TestJdbcConnectionDetails());
|
||||
assertThat(dataSource.getUrl()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUsername()).isEqualTo("user-1");
|
||||
assertThat(dataSource.getPoolProperties().getPassword()).isEqualTo("password-1");
|
||||
assertThat(dataSource.getPoolProperties().getDriverClassName())
|
||||
.isEqualTo(DatabaseDriver.POSTGRESQL.getDriverClassName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import javax.sql.XADataSource;
|
||||
import com.ibm.db2.jcc.DB2XADataSource;
|
||||
import org.hsqldb.jdbc.pool.JDBCXADataSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.postgresql.xa.PGXADataSource;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.jdbc.XADataSourceWrapper;
|
||||
@@ -40,6 +41,8 @@ import static org.mockito.Mockito.mock;
|
||||
* Tests for {@link XADataSourceAutoConfiguration}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class XADataSourceAutoConfigurationTests {
|
||||
|
||||
@@ -90,6 +93,20 @@ class XADataSourceAutoConfigurationTests {
|
||||
assertThat(dataSource.getLoginTimeout()).isEqualTo(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseConnectionDetailsIfAvailable() {
|
||||
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(XADataSourceAutoConfiguration.class))
|
||||
.withUserConfiguration(FromProperties.class, JdbcConnectionDetailsConfiguration.class)
|
||||
.run((context) -> {
|
||||
MockXADataSourceWrapper wrapper = context.getBean(MockXADataSourceWrapper.class);
|
||||
PGXADataSource dataSource = (PGXADataSource) wrapper.getXaDataSource();
|
||||
assertThat(dataSource).isNotNull();
|
||||
assertThat(dataSource.getUrl()).startsWith("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUser()).isEqualTo("user-1");
|
||||
assertThat(dataSource.getPassword()).isEqualTo("password-1");
|
||||
});
|
||||
}
|
||||
|
||||
private ApplicationContext createContext(Class<?> configuration, String... env) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
TestPropertyValues.of(env).applyTo(context);
|
||||
@@ -98,6 +115,16 @@ class XADataSourceAutoConfigurationTests {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JdbcConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails jdbcConnectionDetails() {
|
||||
return new TestJdbcConnectionDetails();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class WrapExisting {
|
||||
|
||||
|
||||
@@ -99,6 +99,9 @@ import static org.mockito.Mockito.never;
|
||||
* @author Nakul Mishra
|
||||
* @author Tomaz Fernandes
|
||||
* @author Thomas Kåsene
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class KafkaAutoConfigurationTests {
|
||||
|
||||
@@ -161,6 +164,24 @@ class KafkaAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectionDetailsAreAppliedToConsumer() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.kafka.bootstrap-servers=foo:1234",
|
||||
"spring.kafka.consumer.bootstrap-servers=foo:1234", "spring.kafka.security.protocol=SSL",
|
||||
"spring.kafka.consumer.security.protocol=SSL")
|
||||
.withBean(KafkaConnectionDetails.class, this::kafkaConnectionDetails)
|
||||
.run((context) -> {
|
||||
DefaultKafkaConsumerFactory<?, ?> consumerFactory = context.getBean(DefaultKafkaConsumerFactory.class);
|
||||
Map<String, Object> configs = consumerFactory.getConfigurationProperties();
|
||||
assertThat(configs).containsEntry(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG,
|
||||
Collections.singletonList("kafka.example.com:12345"));
|
||||
assertThat(configs).containsEntry(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
Collections.singletonList("kafka.example.com:12345"));
|
||||
assertThat(configs).containsEntry(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void producerProperties() {
|
||||
this.contextRunner.withPropertyValues("spring.kafka.clientId=cid",
|
||||
@@ -211,6 +232,24 @@ class KafkaAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectionDetailsAreAppliedToProducer() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.kafka.bootstrap-servers=foo:1234",
|
||||
"spring.kafka.producer.bootstrap-servers=foo:1234", "spring.kafka.security.protocol=SSL",
|
||||
"spring.kafka.producer.security.protocol=SSL")
|
||||
.withBean(KafkaConnectionDetails.class, this::kafkaConnectionDetails)
|
||||
.run((context) -> {
|
||||
DefaultKafkaProducerFactory<?, ?> producerFactory = context.getBean(DefaultKafkaProducerFactory.class);
|
||||
Map<String, Object> configs = producerFactory.getConfigurationProperties();
|
||||
assertThat(configs).containsEntry(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG,
|
||||
Collections.singletonList("kafka.example.com:12345"));
|
||||
assertThat(configs).containsEntry(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
Collections.singletonList("kafka.example.com:12345"));
|
||||
assertThat(configs).containsEntry(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminProperties() {
|
||||
this.contextRunner
|
||||
@@ -252,6 +291,24 @@ class KafkaAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectionDetailsAreAppliedToAdmin() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.kafka.bootstrap-servers=foo:1234", "spring.kafka.security.protocol=SSL",
|
||||
"spring.kafka.admin.security.protocol=SSL")
|
||||
.withBean(KafkaConnectionDetails.class, this::kafkaConnectionDetails)
|
||||
.run((context) -> {
|
||||
KafkaAdmin admin = context.getBean(KafkaAdmin.class);
|
||||
Map<String, Object> configs = admin.getConfigurationProperties();
|
||||
assertThat(configs).containsEntry(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG,
|
||||
Collections.singletonList("kafka.example.com:12345"));
|
||||
assertThat(configs).containsEntry(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
Collections.singletonList("kafka.example.com:12345"));
|
||||
assertThat(configs).containsEntry(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT");
|
||||
assertThat(configs).containsEntry(AdminClientConfig.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT");
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
void streamsProperties() {
|
||||
@@ -298,6 +355,27 @@ class KafkaAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectionDetailsAreAppliedToStreams() {
|
||||
this.contextRunner.withUserConfiguration(EnableKafkaStreamsConfiguration.class)
|
||||
.withPropertyValues("spring.kafka.streams.auto-startup=false", "spring.kafka.streams.application-id=test",
|
||||
"spring.kafka.bootstrap-servers=foo:1234", "spring.kafka.streams.bootstrap-servers=foo:1234",
|
||||
"spring.kafka.security.protocol=SSL", "spring.kafka.streams.security.protocol=SSL")
|
||||
.withBean(KafkaConnectionDetails.class, this::kafkaConnectionDetails)
|
||||
.run((context) -> {
|
||||
Properties configs = context
|
||||
.getBean(KafkaStreamsDefaultConfiguration.DEFAULT_STREAMS_CONFIG_BEAN_NAME,
|
||||
KafkaStreamsConfiguration.class)
|
||||
.asProperties();
|
||||
assertThat(configs).containsEntry(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG,
|
||||
Collections.singletonList("kafka.example.com:12345"));
|
||||
assertThat(configs).containsEntry(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
Collections.singletonList("kafka.example.com:12345"));
|
||||
assertThat(configs).containsEntry(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT");
|
||||
assertThat(configs).containsEntry(StreamsConfig.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT");
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Deprecated(since = "3.1.0", forRemoval = true)
|
||||
void streamsCacheMaxSizeBuffering() {
|
||||
@@ -744,6 +822,17 @@ class KafkaAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
private KafkaConnectionDetails kafkaConnectionDetails() {
|
||||
return new KafkaConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public List<Node> getBootstrapNodes() {
|
||||
return List.of(new Node("kafka.example.com", 12345));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class MessageConverterConfiguration {
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.util.function.Consumer;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import liquibase.integration.spring.SpringLiquibase;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -39,6 +40,7 @@ import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseAutoConfigurationRuntimeHints;
|
||||
@@ -75,6 +77,8 @@ import static org.assertj.core.api.Assertions.contentOf;
|
||||
* @author Andrii Hrytsiuk
|
||||
* @author Ferenc Gratzer
|
||||
* @author Evgeniy Cheban
|
||||
* @author Moritz Halbritter
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class LiquibaseAutoConfigurationTests {
|
||||
@@ -97,6 +101,18 @@ class LiquibaseAutoConfigurationTests {
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsDataSourceWithNoDataSourceBeanAndJdbcConnectionDetails() {
|
||||
this.contextRunner.withSystemProperties("shouldRun=false")
|
||||
.withUserConfiguration(JdbcConnectionDetailsConfiguration.class)
|
||||
.run(assertLiquibase((liquibase) -> {
|
||||
SimpleDriverDataSource dataSource = (SimpleDriverDataSource) liquibase.getDataSource();
|
||||
assertThat(dataSource.getUrl()).isEqualTo("jdbc:postgresql://database.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUsername()).isEqualTo("user-1");
|
||||
assertThat(dataSource.getPassword()).isEqualTo("secret-1");
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void backsOffWithLiquibaseUrlAndNoSpringJdbc() {
|
||||
this.contextRunner.withPropertyValues("spring.liquibase.url:jdbc:hsqldb:mem:" + UUID.randomUUID())
|
||||
@@ -116,6 +132,30 @@ class LiquibaseAutoConfigurationTests {
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void jdbcConnectionDetailsAreUsedIfAvailable() {
|
||||
this.contextRunner.withSystemProperties("shouldRun=false")
|
||||
.withUserConfiguration(EmbeddedDataSourceConfiguration.class, JdbcConnectionDetailsConfiguration.class)
|
||||
.run(assertLiquibase((liquibase) -> {
|
||||
SimpleDriverDataSource dataSource = (SimpleDriverDataSource) liquibase.getDataSource();
|
||||
assertThat(dataSource.getUrl()).isEqualTo("jdbc:postgresql://database.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUsername()).isEqualTo("user-1");
|
||||
assertThat(dataSource.getPassword()).isEqualTo("secret-1");
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void liquibaseDataSourceIsUsedOverJdbcConnectionDetails() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(LiquibaseDataSourceConfiguration.class, JdbcConnectionDetailsConfiguration.class)
|
||||
.run(assertLiquibase((liquibase) -> {
|
||||
HikariDataSource dataSource = (HikariDataSource) liquibase.getDataSource();
|
||||
assertThat(dataSource.getJdbcUrl()).startsWith("jdbc:hsqldb:mem:liquibasetest");
|
||||
assertThat(dataSource.getUsername()).isEqualTo("sa");
|
||||
assertThat(dataSource.getPassword()).isNull();
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void changelogXml() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
@@ -509,6 +549,33 @@ class LiquibaseAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JdbcConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails jdbcConnectionDetails() {
|
||||
return new JdbcConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return "jdbc:postgresql://database.example.com:12345/database-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "secret-1";
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CustomH2Driver extends org.h2.Driver {
|
||||
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import static org.mockito.Mockito.mock;
|
||||
* @author Mark Paluch
|
||||
* @author Artsiom Yudovin
|
||||
* @author Scott Frederick
|
||||
* @author Mortiz Halbritter
|
||||
*/
|
||||
abstract class MongoClientFactorySupportTests<T> {
|
||||
|
||||
@@ -110,10 +111,6 @@ abstract class MongoClientFactorySupportTests<T> {
|
||||
assertThat(properties.isAutoIndexCreation()).isTrue();
|
||||
}
|
||||
|
||||
protected T createMongoClient() {
|
||||
return createMongoClient(null, MongoClientSettings.builder().build());
|
||||
}
|
||||
|
||||
protected T createMongoClient(MongoClientSettings settings) {
|
||||
return createMongoClient(null, settings);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -32,6 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
@Deprecated(since = "3.1.0", forRemoval = true)
|
||||
class MongoPropertiesClientSettingsBuilderCustomizerTests {
|
||||
|
||||
private final MongoProperties properties = new MongoProperties();
|
||||
@@ -188,6 +189,7 @@ class MongoPropertiesClientSettingsBuilderCustomizerTests {
|
||||
assertThat(settings.getRetryWrites()).isFalse();
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private MongoClientSettings customizeSettings() {
|
||||
MongoClientSettings.Builder settings = MongoClientSettings.builder();
|
||||
new MongoPropertiesClientSettingsBuilderCustomizer(this.properties).customize(settings);
|
||||
|
||||
@@ -26,20 +26,19 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.neo4j.driver.AuthToken;
|
||||
import org.neo4j.driver.AuthTokens;
|
||||
import org.neo4j.driver.Config;
|
||||
import org.neo4j.driver.Config.ConfigBuilder;
|
||||
import org.neo4j.driver.Driver;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration.PropertiesNeo4jConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jProperties.Authentication;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jProperties.Security.TrustStrategy;
|
||||
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
@@ -50,6 +49,9 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class Neo4jAutoConfigurationTests {
|
||||
|
||||
@@ -103,6 +105,22 @@ class Neo4jAutoConfigurationTests {
|
||||
.hasMessageContaining("'%s' is not a supported scheme.", invalidScheme));
|
||||
}
|
||||
|
||||
@Bean
|
||||
void usesCustomConnectionDetails() {
|
||||
this.contextRunner.withBean(Neo4jConnectionDetails.class, () -> new Neo4jConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
return URI.create("bolt+ssc://localhost:12345");
|
||||
}
|
||||
|
||||
}).run((context) -> {
|
||||
assertThat(context).hasSingleBean(Driver.class);
|
||||
Driver driver = context.getBean(Driver.class);
|
||||
assertThat(driver.isEncrypted()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectionTimeout() {
|
||||
Neo4jProperties properties = new Neo4jProperties();
|
||||
@@ -118,8 +136,8 @@ class Neo4jAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void determineServerUriShouldDefaultToLocalhost() {
|
||||
assertThat(determineServerUri(new Neo4jProperties(), new MockEnvironment()))
|
||||
void uriShouldDefaultToLocalhost() {
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(new Neo4jProperties()).getUri())
|
||||
.isEqualTo(URI.create("bolt://localhost:7687"));
|
||||
}
|
||||
|
||||
@@ -128,44 +146,52 @@ class Neo4jAutoConfigurationTests {
|
||||
URI customUri = URI.create("bolt://localhost:4242");
|
||||
Neo4jProperties properties = new Neo4jProperties();
|
||||
properties.setUri(customUri);
|
||||
assertThat(determineServerUri(properties, new MockEnvironment())).isEqualTo(customUri);
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(properties).getUri()).isEqualTo(customUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticationShouldDefaultToNone() {
|
||||
assertThat(mapAuthToken(new Authentication())).isEqualTo(AuthTokens.none());
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(new Neo4jProperties()).getAuthToken())
|
||||
.isEqualTo(AuthTokens.none());
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticationWithUsernameShouldEnableBasicAuth() {
|
||||
Authentication authentication = new Authentication();
|
||||
authentication.setUsername("Farin");
|
||||
authentication.setPassword("Urlaub");
|
||||
assertThat(mapAuthToken(authentication)).isEqualTo(AuthTokens.basic("Farin", "Urlaub"));
|
||||
Neo4jProperties properties = new Neo4jProperties();
|
||||
properties.getAuthentication().setUsername("Farin");
|
||||
properties.getAuthentication().setPassword("Urlaub");
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(properties).getAuthToken())
|
||||
.isEqualTo(AuthTokens.basic("Farin", "Urlaub"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticationWithUsernameAndRealmShouldEnableBasicAuth() {
|
||||
Authentication authentication = new Authentication();
|
||||
Neo4jProperties properties = new Neo4jProperties();
|
||||
Authentication authentication = properties.getAuthentication();
|
||||
authentication.setUsername("Farin");
|
||||
authentication.setPassword("Urlaub");
|
||||
authentication.setRealm("Test Realm");
|
||||
assertThat(mapAuthToken(authentication)).isEqualTo(AuthTokens.basic("Farin", "Urlaub", "Test Realm"));
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(properties).getAuthToken())
|
||||
.isEqualTo(AuthTokens.basic("Farin", "Urlaub", "Test Realm"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticationWithKerberosTicketShouldEnableKerberos() {
|
||||
Authentication authentication = new Authentication();
|
||||
Neo4jProperties properties = new Neo4jProperties();
|
||||
Authentication authentication = properties.getAuthentication();
|
||||
authentication.setKerberosTicket("AABBCCDDEE");
|
||||
assertThat(mapAuthToken(authentication)).isEqualTo(AuthTokens.kerberos("AABBCCDDEE"));
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(properties).getAuthToken())
|
||||
.isEqualTo(AuthTokens.kerberos("AABBCCDDEE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticationWithBothUsernameAndKerberosShouldNotBeAllowed() {
|
||||
Authentication authentication = new Authentication();
|
||||
Neo4jProperties properties = new Neo4jProperties();
|
||||
Authentication authentication = properties.getAuthentication();
|
||||
authentication.setUsername("Farin");
|
||||
authentication.setKerberosTicket("AABBCCDDEE");
|
||||
assertThatIllegalStateException().isThrownBy(() -> mapAuthToken(authentication))
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> new PropertiesNeo4jConnectionDetails(properties).getAuthToken())
|
||||
.withMessage("Cannot specify both username ('Farin') and kerberos ticket ('AABBCCDDEE')");
|
||||
}
|
||||
|
||||
@@ -279,20 +305,9 @@ class Neo4jAutoConfigurationTests {
|
||||
assertThat(mapDriverConfig(new Neo4jProperties()).logging()).isInstanceOf(Neo4jSpringJclLogging.class);
|
||||
}
|
||||
|
||||
private URI determineServerUri(Neo4jProperties properties, Environment environment) {
|
||||
return new Neo4jAutoConfiguration().determineServerUri(properties, environment);
|
||||
}
|
||||
|
||||
private AuthToken mapAuthToken(Authentication authentication, Environment environment) {
|
||||
return new Neo4jAutoConfiguration().mapAuthToken(authentication, environment);
|
||||
}
|
||||
|
||||
private AuthToken mapAuthToken(Authentication authentication) {
|
||||
return mapAuthToken(authentication, new MockEnvironment());
|
||||
}
|
||||
|
||||
private Config mapDriverConfig(Neo4jProperties properties, ConfigBuilderCustomizer... customizers) {
|
||||
return new Neo4jAutoConfiguration().mapDriverConfig(properties, Arrays.asList(customizers));
|
||||
return new Neo4jAutoConfiguration().mapDriverConfig(properties,
|
||||
new PropertiesNeo4jConnectionDetails(properties), Arrays.asList(customizers));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import io.r2dbc.h2.H2ConnectionFactory;
|
||||
import io.r2dbc.pool.ConnectionPool;
|
||||
import io.r2dbc.pool.PoolMetrics;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.ConnectionFactoryOptions;
|
||||
import io.r2dbc.spi.ConnectionFactoryProvider;
|
||||
import io.r2dbc.spi.Option;
|
||||
import io.r2dbc.spi.Wrapped;
|
||||
@@ -54,6 +55,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class R2dbcAutoConfigurationTests {
|
||||
|
||||
@@ -68,7 +72,7 @@ class R2dbcAutoConfigurationTests {
|
||||
assertThat(context.getBean(ConnectionPool.class)).extracting(ConnectionPool::unwrap)
|
||||
.satisfies((connectionFactory) -> assertThat(connectionFactory)
|
||||
.asInstanceOf(type(OptionsCapableConnectionFactory.class))
|
||||
.extracting(Wrapped<ConnectionFactory>::unwrap)
|
||||
.extracting(Wrapped::unwrap)
|
||||
.isExactlyInstanceOf(H2ConnectionFactory.class));
|
||||
});
|
||||
}
|
||||
@@ -306,6 +310,64 @@ class R2dbcAutoConfigurationTests {
|
||||
.doesNotHaveBean(DatabaseClient.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseCustomConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withPropertyValues("spring.r2dbc.pool.enabled=false")
|
||||
.withUserConfiguration(ConnectionDetailsConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(ConnectionFactory.class);
|
||||
OptionsCapableConnectionFactory connectionFactory = context
|
||||
.getBean(OptionsCapableConnectionFactory.class);
|
||||
ConnectionFactoryOptions options = connectionFactory.getOptions();
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.DRIVER)).isEqualTo("postgresql");
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.HOST)).isEqualTo("postgres.example.com");
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.PORT)).isEqualTo(12345);
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.DATABASE)).isEqualTo("database-1");
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.USER)).isEqualTo("user-1");
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.PASSWORD)).isEqualTo("password-1");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void configureWithUsernamePasswordAndUrlWithoutUserInfoUsesUsernameAndPassword() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.r2dbc.pool.enabled=false",
|
||||
"spring.r2dbc.url:r2dbc:postgresql://postgres.example.com:4321/db", "spring.r2dbc.username=alice",
|
||||
"spring.r2dbc.password=secret")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(ConnectionFactory.class);
|
||||
OptionsCapableConnectionFactory connectionFactory = context
|
||||
.getBean(OptionsCapableConnectionFactory.class);
|
||||
ConnectionFactoryOptions options = connectionFactory.getOptions();
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.DRIVER)).isEqualTo("postgresql");
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.HOST)).isEqualTo("postgres.example.com");
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.PORT)).isEqualTo(4321);
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.DATABASE)).isEqualTo("db");
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.USER)).isEqualTo("alice");
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.PASSWORD)).isEqualTo("secret");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void configureWithUsernamePasswordAndUrlWithUserInfoUsesUserInfo() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.r2dbc.pool.enabled=false",
|
||||
"spring.r2dbc.url:r2dbc:postgresql://bob:password@postgres.example.com:9876/db",
|
||||
"spring.r2dbc.username=alice", "spring.r2dbc.password=secret")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(ConnectionFactory.class);
|
||||
OptionsCapableConnectionFactory connectionFactory = context
|
||||
.getBean(OptionsCapableConnectionFactory.class);
|
||||
ConnectionFactoryOptions options = connectionFactory.getOptions();
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.DRIVER)).isEqualTo("postgresql");
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.HOST)).isEqualTo("postgres.example.com");
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.PORT)).isEqualTo(9876);
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.DATABASE)).isEqualTo("db");
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.USER)).isEqualTo("bob");
|
||||
assertThat(options.getValue(ConnectionFactoryOptions.PASSWORD)).isEqualTo("password");
|
||||
});
|
||||
}
|
||||
|
||||
private <T> InstanceOfAssertFactory<T, ObjectAssert<T>> type(Class<T> type) {
|
||||
return InstanceOfAssertFactories.type(type);
|
||||
}
|
||||
@@ -342,4 +404,22 @@ class R2dbcAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
R2dbcConnectionDetails r2dbcConnectionDetails() {
|
||||
return new R2dbcConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public ConnectionFactoryOptions getConnectionFactoryOptions() {
|
||||
return ConnectionFactoryOptions
|
||||
.parse("r2dbc:postgresql://user-1:password-1@postgres.example.com:12345/database-1");
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user