diff --git a/docs/src/main/asciidoc/spring-cloud-config.adoc b/docs/src/main/asciidoc/spring-cloud-config.adoc
index 5ba5a368..d5982942 100644
--- a/docs/src/main/asciidoc/spring-cloud-config.adoc
+++ b/docs/src/main/asciidoc/spring-cloud-config.adoc
@@ -757,6 +757,52 @@ The database needs to have a table called `PROPERTIES` with columns called `APPL
All fields are of type String in Java, so you can make them `VARCHAR` of whatever length you need.
Property values behave in the same way as they would if they came from Spring Boot properties files named `{application}-{profile}.properties`, including all the encryption and decryption, which will be applied as post-processing steps (that is, not in the repository implementation directly).
+==== Redis Backend
+
+Spring Cloud Config Server supports Redis as a backend for configuration properties.
+You can enable this feature by adding a dependency to link:https://spring.io/projects/spring-data-redis[Spring Data Redis].
+
+[source,xml,indent=0]
+.pom.xml
+----
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+----
+
+The following configuration uses Spring Data `RedisTemplate` to access a Redis. We can use `spring.redis.*` properties to override default connection settings.
+
+[source,yaml]
+----
+spring:
+ profiles:
+ active: redis
+ redis:
+ host: redis
+ port: 16379
+----
+
+The properties should be stored as fields in a hash. The name of hash should be the same as `spring.application.name` property or conjunction of `spring.application.name` and `spring.profiles.active[n]`.
+[source,sh]
+----
+HMSET sample-app server.port "8100" sample.topic.name "test" test.property1 "property1"
+----
+
+After executing the command visible above a hash should contain the following keys with values:
+----
+HGETALL sample-app
+{
+ "server.port": "8100",
+ "sample.topic.name": "test",
+ "test.property1": "property1"
+}
+----
+
+NOTE: When no profile is specified `default` will be used.
+
==== CredHub Backend
Spring Cloud Config Server supports link:https://docs.cloudfoundry.org/credhub[CredHub] as a backend for configuration properties.
diff --git a/spring-cloud-config-server/pom.xml b/spring-cloud-config-server/pom.xml
index 4dd71cfc..1ba3b63f 100644
--- a/spring-cloud-config-server/pom.xml
+++ b/spring-cloud-config-server/pom.xml
@@ -28,6 +28,11 @@
spring-boot-starter-jdbc
true
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+ true
+
org.springframework.cloud
spring-cloud-config-client
@@ -100,6 +105,12 @@
org.eclipse.jgit.junit.http
test
+
+ it.ozimov
+ embedded-redis
+ 0.7.2
+ test
+
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java
index 396d82b0..09b9b482 100644
--- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java
@@ -54,6 +54,9 @@ import org.springframework.cloud.config.server.environment.MultipleJGitEnvironme
import org.springframework.cloud.config.server.environment.NativeEnvironmentProperties;
import org.springframework.cloud.config.server.environment.NativeEnvironmentRepository;
import org.springframework.cloud.config.server.environment.NativeEnvironmentRepositoryFactory;
+import org.springframework.cloud.config.server.environment.RedisEnvironmentProperties;
+import org.springframework.cloud.config.server.environment.RedisEnvironmentRepository;
+import org.springframework.cloud.config.server.environment.RedisEnvironmentRepositoryFactory;
import org.springframework.cloud.config.server.environment.SearchPathCompositeEnvironmentRepository;
import org.springframework.cloud.config.server.environment.SvnEnvironmentRepositoryFactory;
import org.springframework.cloud.config.server.environment.SvnKitEnvironmentProperties;
@@ -69,6 +72,7 @@ import org.springframework.context.annotation.Profile;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.credhub.core.CredHubOperations;
+import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
/**
@@ -81,12 +85,13 @@ import org.springframework.jdbc.core.JdbcTemplate;
@Configuration
@EnableConfigurationProperties({ SvnKitEnvironmentProperties.class,
CredhubEnvironmentProperties.class, JdbcEnvironmentProperties.class,
- NativeEnvironmentProperties.class, VaultEnvironmentProperties.class })
+ NativeEnvironmentProperties.class, VaultEnvironmentProperties.class,
+ RedisEnvironmentProperties.class })
@Import({ CompositeRepositoryConfiguration.class, JdbcRepositoryConfiguration.class,
VaultRepositoryConfiguration.class, CredhubConfiguration.class,
CredhubRepositoryConfiguration.class, SvnRepositoryConfiguration.class,
NativeRepositoryConfiguration.class, GitRepositoryConfiguration.class,
- DefaultRepositoryConfiguration.class })
+ RedisRepositoryConfiguration.class, DefaultRepositoryConfiguration.class })
public class EnvironmentRepositoryConfiguration {
@Bean
@@ -199,6 +204,19 @@ public class EnvironmentRepositoryConfiguration {
}
+ @Configuration
+ @ConditionalOnClass(StringRedisTemplate.class)
+ static class RedisFactoryConfig {
+
+ @Bean
+ @ConditionalOnBean(StringRedisTemplate.class)
+ public RedisEnvironmentRepositoryFactory redisEnvironmentRepositoryFactory(
+ StringRedisTemplate redis) {
+ return new RedisEnvironmentRepositoryFactory(redis);
+ }
+
+ }
+
@Configuration
@ConditionalOnClass(CredHubOperations.class)
static class CredhubFactoryConfig {
@@ -312,6 +330,21 @@ class JdbcRepositoryConfiguration {
}
+@Configuration
+@Profile("redis")
+@ConditionalOnClass(StringRedisTemplate.class)
+class RedisRepositoryConfiguration {
+
+ @Bean
+ @ConditionalOnBean(StringRedisTemplate.class)
+ public RedisEnvironmentRepository redisEnvironmentRepository(
+ RedisEnvironmentRepositoryFactory factory,
+ RedisEnvironmentProperties environmentProperties) {
+ return factory.build(environmentProperties);
+ }
+
+}
+
@Configuration
@Profile("composite")
class CompositeRepositoryConfiguration {
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/RedisEnvironmentProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/RedisEnvironmentProperties.java
new file mode 100644
index 00000000..a2bb5271
--- /dev/null
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/RedisEnvironmentProperties.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.config.server.environment;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.config.server.support.EnvironmentRepositoryProperties;
+import org.springframework.core.Ordered;
+
+/**
+ * @author Piotr Mińkowski
+ */
+@ConfigurationProperties("spring.cloud.config.server.redis")
+public class RedisEnvironmentProperties implements EnvironmentRepositoryProperties {
+
+ private int order = Ordered.LOWEST_PRECEDENCE;
+
+ public int getOrder() {
+ return this.order;
+ }
+
+ @Override
+ public void setOrder(int order) {
+ this.order = order;
+ }
+
+}
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/RedisEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/RedisEnvironmentRepository.java
new file mode 100644
index 00000000..2435baef
--- /dev/null
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/RedisEnvironmentRepository.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.config.server.environment;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.cloud.config.environment.Environment;
+import org.springframework.cloud.config.environment.PropertySource;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.util.StringUtils;
+
+/**
+ * @author Piotr Mińkowski
+ */
+public class RedisEnvironmentRepository implements EnvironmentRepository {
+
+ private final StringRedisTemplate redis;
+
+ private final RedisEnvironmentProperties properties;
+
+ public RedisEnvironmentRepository(StringRedisTemplate redis,
+ RedisEnvironmentProperties properties) {
+ this.redis = redis;
+ this.properties = properties;
+ }
+
+ @Override
+ public Environment findOne(String application, String profile, String label) {
+ String[] profiles = StringUtils.commaDelimitedListToStringArray(profile);
+ Environment environment = new Environment(application, profiles, label, null,
+ null);
+ final List keys = addKeys(application, Arrays.asList(profiles));
+ keys.forEach(it -> {
+ Map, ?> m = redis.opsForHash().entries(it);
+ environment.add(new PropertySource("redis:" + it, m));
+ });
+ return environment;
+ }
+
+ private List addKeys(String application, List profiles) {
+ List keys = new ArrayList<>();
+ keys.add(application);
+ for (String profile : profiles) {
+ keys.add(application + "-" + profile);
+ }
+ Collections.reverse(keys);
+ return keys;
+ }
+
+}
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/RedisEnvironmentRepositoryFactory.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/RedisEnvironmentRepositoryFactory.java
new file mode 100644
index 00000000..924d6fc0
--- /dev/null
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/RedisEnvironmentRepositoryFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.config.server.environment;
+
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+/**
+ * @author Piotr Mińkowski
+ */
+public class RedisEnvironmentRepositoryFactory implements
+ EnvironmentRepositoryFactory {
+
+ private StringRedisTemplate redis;
+
+ public RedisEnvironmentRepositoryFactory(StringRedisTemplate redis) {
+ this.redis = redis;
+ }
+
+ @Override
+ public RedisEnvironmentRepository build(
+ RedisEnvironmentProperties environmentProperties) {
+ return new RedisEnvironmentRepository(this.redis, environmentProperties);
+ }
+
+}
diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeClasspathTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeClasspathTests.java
index fdf1bc50..0770ab12 100644
--- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeClasspathTests.java
+++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeClasspathTests.java
@@ -27,7 +27,7 @@ import org.springframework.cloud.test.ModifiedClassPathRunner;
public class CompositeClasspathTests {
@RunWith(ModifiedClassPathRunner.class)
- @ClassPathExclusions("spring-jdbc-*.jar")
+ @ClassPathExclusions({"spring-jdbc-*.jar", "spring-data-redis-*.jar"})
public static class JdbcTests {
@Test
diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/RedisEnvironmentRepositoryIntegrationTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/RedisEnvironmentRepositoryIntegrationTests.java
new file mode 100644
index 00000000..ad1fd542
--- /dev/null
+++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/RedisEnvironmentRepositoryIntegrationTests.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.config.server.environment;
+
+import java.io.IOException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import redis.embedded.RedisServer;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;
+import org.springframework.cloud.config.environment.Environment;
+import org.springframework.data.redis.core.BoundHashOperations;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@DataRedisTest
+@ActiveProfiles("redis")
+public class RedisEnvironmentRepositoryIntegrationTests {
+
+ private RedisServer redisServer;
+
+ @Before
+ public void startRedis() throws IOException {
+ redisServer = new RedisServer(6379);
+ redisServer.start();
+ }
+
+ @After
+ public void stopRedis() {
+ redisServer.stop();
+ }
+
+ @Autowired
+ StringRedisTemplate redis;
+
+ @Test
+ public void test() {
+ BoundHashOperations bound = redis.boundHashOps("foo-bar");
+ bound.put("name", "foo");
+ bound.put("tag", "myapp");
+
+ Environment env = new RedisEnvironmentRepository(redis,
+ new RedisEnvironmentProperties()).findOne("foo", "bar", "");
+ assertThat(env.getName()).isEqualTo("foo");
+ assertThat(env.getPropertySources()).isNotEmpty();
+ assertThat(env.getPropertySources().get(0).getSource().get("tag"))
+ .isEqualTo("myapp");
+ }
+
+}