From 904feb249006cd28464e0eef7da9dad43ced20fc Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 22 Jun 2022 18:59:51 -0700 Subject: [PATCH] Polish 'Allow spring.data.cassandra.config file to override default values' See gh-31238 --- .../cassandra/CassandraAutoConfiguration.java | 54 +++++++++++-------- .../cassandra/CassandraProperties.java | 12 ----- ...itional-spring-configuration-metadata.json | 4 ++ .../CassandraAutoConfigurationTests.java | 53 +++++++----------- .../cassandra/override-defaults.conf | 36 ++++++------- 5 files changed, 73 insertions(+), 86 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java index a36fed6b20..8907a22633 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.cassandra; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.time.Duration; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -63,6 +64,7 @@ import org.springframework.core.io.Resource; * @author Eddú Meléndez * @author Stephane Nicoll * @author Steffen F. Qvistgaard + * @author Ittay Stern * @since 1.3.0 */ @Configuration(proxyBeanMethods = false) @@ -70,6 +72,15 @@ import org.springframework.core.io.Resource; @EnableConfigurationProperties(CassandraProperties.class) public class CassandraAutoConfiguration { + private static final Config SPRING_BOOT_DEFAULTS; + static { + CassandraDriverOptions options = new CassandraDriverOptions(); + options.add(DefaultDriverOption.CONTACT_POINTS, Collections.singletonList("127.0.0.1:9042")); + options.add(DefaultDriverOption.PROTOCOL_COMPRESSION, "none"); + options.add(DefaultDriverOption.CONTROL_CONNECTION_TIMEOUT, (int) Duration.ofSeconds(5).toMillis()); + SPRING_BOOT_DEFAULTS = options.build(); + } + @Bean @ConditionalOnMissingBean @Lazy @@ -118,43 +129,41 @@ public class CassandraAutoConfiguration { } private Config cassandraConfiguration(CassandraProperties properties) { - Config config = mapConfig(properties); - Resource configFile = properties.getConfig(); - return (configFile != null) ? applyDefaultFallback(config.withFallback(loadConfig(configFile))) - : applyDefaultFallback(config); - } - - private Config applyDefaultFallback(Config config) { ConfigFactory.invalidateCaches(); - return ConfigFactory.defaultOverrides().withFallback(config) - .withFallback(mapConfig(CassandraProperties.defaults())).withFallback(ConfigFactory.defaultReference()) - .resolve(); + Config config = ConfigFactory.defaultOverrides(); + config = config.withFallback(mapConfig(properties)); + if (properties.getConfig() != null) { + config = config.withFallback(loadConfig(properties.getConfig())); + } + config = config.withFallback(SPRING_BOOT_DEFAULTS); + config = config.withFallback(ConfigFactory.defaultReference()); + return config.resolve(); } - private Config loadConfig(Resource config) { + private Config loadConfig(Resource resource) { try { - return ConfigFactory.parseURL(config.getURL()); + return ConfigFactory.parseURL(resource.getURL()); } catch (IOException ex) { - throw new IllegalStateException("Failed to load cassandra configuration from " + config, ex); + throw new IllegalStateException("Failed to load cassandra configuration from " + resource, ex); } } private Config mapConfig(CassandraProperties properties) { CassandraDriverOptions options = new CassandraDriverOptions(); - PropertyMapper map = PropertyMapper.get(); + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); map.from(properties.getSessionName()).whenHasText() .to((sessionName) -> options.add(DefaultDriverOption.SESSION_NAME, sessionName)); - map.from(properties::getUsername).whenNonNull() + 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).whenNonNull() + map.from(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)).whenNonNull() + map.from(mapContactPoints(properties)) .to((contactPoints) -> options.add(DefaultDriverOption.CONTACT_POINTS, contactPoints)); map.from(properties.getLocalDatacenter()).whenHasText().to( (localDatacenter) -> options.add(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER, localDatacenter)); @@ -211,9 +220,12 @@ public class CassandraAutoConfiguration { } private List mapContactPoints(CassandraProperties properties) { - List contactPoints = properties.getContactPoints(); - return (contactPoints == null) ? null : contactPoints.stream() - .map((candidate) -> formatContactPoint(candidate, properties.getPort())).collect(Collectors.toList()); + if (properties.getContactPoints() != null) { + return properties.getContactPoints().stream() + .map((candidate) -> formatContactPoint(candidate, properties.getPort())) + .collect(Collectors.toList()); + } + return null; } private String formatContactPoint(String candidate, int port) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java index f8a5477e5d..9d27303871 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java @@ -17,8 +17,6 @@ package org.springframework.boot.autoconfigure.cassandra; import java.time.Duration; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import com.datastax.oss.driver.api.core.DefaultConsistencyLevel; @@ -38,16 +36,6 @@ import org.springframework.core.io.Resource; @ConfigurationProperties(prefix = "spring.data.cassandra") public class CassandraProperties { - static CassandraProperties defaults() { - CassandraProperties properties = new CassandraProperties(); - - properties.setContactPoints(new ArrayList<>(Collections.singleton("127.0.0.1:9042"))); - properties.setCompression(Compression.NONE); - properties.getControlconnection().setTimeout(Duration.ofSeconds(5)); - - return properties; - } - /** * Location of the configuration file to use. */ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 1591c65a84..7d2333fb69 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -492,6 +492,10 @@ "name": "spring.data.cassandra.connection.init-query-timeout", "defaultValue": "5s" }, + { + "name": "spring.data.cassandra.controlconnection.timeout", + "defaultValue": "5s" + }, { "name": "spring.data.cassandra.contact-points", "defaultValue": [ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationTests.java index 1975e4ff80..971ec02b7e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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,7 +18,6 @@ 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; @@ -30,7 +29,6 @@ import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; import com.datastax.oss.driver.internal.core.session.throttling.ConcurrencyLimitingRequestThrottler; import com.datastax.oss.driver.internal.core.session.throttling.PassThroughRequestThrottler; import com.datastax.oss.driver.internal.core.session.throttling.RateLimitingRequestThrottler; -import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -46,6 +44,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * * @author Eddú Meléndez * @author Stephane Nicoll + * @author Ittay Stern */ class CassandraAutoConfigurationTests { @@ -247,41 +246,25 @@ class CassandraAutoConfigurationTests { }); } - @Test // gh-31025 + @Test // gh-31238 void driverConfigLoaderWithConfigOverridesDefaults() { String configLocation = "org/springframework/boot/autoconfigure/cassandra/override-defaults.conf"; this.contextRunner.withPropertyValues("spring.data.cassandra.config=" + configLocation).run((context) -> { - assertThat(context).hasSingleBean(DriverConfigLoader.class); - - SoftAssertions softly = new SoftAssertions(); - - softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile() - .getString(DefaultDriverOption.SESSION_NAME)).isEqualTo("advanced session"); - - softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile() - .getDuration(DefaultDriverOption.REQUEST_TIMEOUT)).isEqualTo(Duration.ofSeconds(2)); // default - - softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile() - .getStringList(DefaultDriverOption.CONTACT_POINTS)).isEqualTo(Collections.singletonList("1.2.3.4:5678")); - softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile() - .getBoolean(DefaultDriverOption.RESOLVE_CONTACT_POINTS)).isFalse(); - softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile() - .getInt(DefaultDriverOption.REQUEST_PAGE_SIZE)).isEqualTo(11); - softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile() - .getString(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER)).isEqualTo("datacenter1"); - - softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile() - .getInt(DefaultDriverOption.REQUEST_THROTTLER_MAX_CONCURRENT_REQUESTS)).isEqualTo(22); - softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile() - .getInt(DefaultDriverOption.REQUEST_THROTTLER_MAX_REQUESTS_PER_SECOND)).isEqualTo(33); - softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile() - .getInt(DefaultDriverOption.REQUEST_THROTTLER_MAX_QUEUE_SIZE)).isEqualTo(44); - softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile() - .getDuration(DefaultDriverOption.CONTROL_CONNECTION_TIMEOUT)).isEqualTo(Duration.ofMillis(5555)); - softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile() - .getString(DefaultDriverOption.PROTOCOL_COMPRESSION)).isEqualTo("SNAPPY"); - - softly.assertAll(); + DriverExecutionProfile actual = context.getBean(DriverConfigLoader.class).getInitialConfig() + .getDefaultProfile(); + assertThat(actual.getString(DefaultDriverOption.SESSION_NAME)).isEqualTo("advanced session"); + assertThat(actual.getDuration(DefaultDriverOption.REQUEST_TIMEOUT)).isEqualTo(Duration.ofSeconds(2)); + assertThat(actual.getStringList(DefaultDriverOption.CONTACT_POINTS)) + .isEqualTo(Collections.singletonList("1.2.3.4:5678")); + assertThat(actual.getBoolean(DefaultDriverOption.RESOLVE_CONTACT_POINTS)).isFalse(); + assertThat(actual.getInt(DefaultDriverOption.REQUEST_PAGE_SIZE)).isEqualTo(11); + assertThat(actual.getString(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER)).isEqualTo("datacenter1"); + assertThat(actual.getInt(DefaultDriverOption.REQUEST_THROTTLER_MAX_CONCURRENT_REQUESTS)).isEqualTo(22); + assertThat(actual.getInt(DefaultDriverOption.REQUEST_THROTTLER_MAX_REQUESTS_PER_SECOND)).isEqualTo(33); + assertThat(actual.getInt(DefaultDriverOption.REQUEST_THROTTLER_MAX_QUEUE_SIZE)).isEqualTo(44); + assertThat(actual.getDuration(DefaultDriverOption.CONTROL_CONNECTION_TIMEOUT)) + .isEqualTo(Duration.ofMillis(5555)); + assertThat(actual.getString(DefaultDriverOption.PROTOCOL_COMPRESSION)).isEqualTo("SNAPPY"); }); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/cassandra/override-defaults.conf b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/cassandra/override-defaults.conf index 52115731ed..857df202fc 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/cassandra/override-defaults.conf +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/cassandra/override-defaults.conf @@ -1,20 +1,20 @@ datastax-java-driver { - basic { - session-name = advanced session - load-balancing-policy { - local-datacenter = datacenter1 - } - request.page-size = 11 - contact-points = [ "1.2.3.4:5678" ] - } - advanced { - throttler { - max-concurrent-requests = 22 - max-requests-per-second = 33 - max-queue-size = 44 - } - control-connection.timeout = 5555 - protocol.compression = SNAPPY - resolve-contact-points = false - } + basic { + session-name = advanced session + load-balancing-policy { + local-datacenter = datacenter1 + } + request.page-size = 11 + contact-points = [ "1.2.3.4:5678" ] + } + advanced { + throttler { + max-concurrent-requests = 22 + max-requests-per-second = 33 + max-queue-size = 44 + } + control-connection.timeout = 5555 + protocol.compression = SNAPPY + resolve-contact-points = false + } }