Skip to content

Commit 7d495b4

Browse files
committed
Allow cache configuration from file
1 parent b975572 commit 7d495b4

File tree

3 files changed

+180
-0
lines changed

3 files changed

+180
-0
lines changed

src/main/java/org/springframework/data/redis/cache/RedisCacheConfiguration.java

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@
1515
*/
1616
package org.springframework.data.redis.cache;
1717

18+
import java.io.IOException;
19+
import java.io.InputStream;
1820
import java.nio.charset.StandardCharsets;
1921
import java.time.Duration;
22+
import java.util.Properties;
2023
import java.util.function.Consumer;
24+
import java.util.logging.Logger;
25+
import java.io.FileNotFoundException;
2126

2227
import org.springframework.cache.Cache;
2328
import org.springframework.cache.interceptor.SimpleKey;
@@ -42,6 +47,7 @@
4247
* @author Christoph Strobl
4348
* @author Mark Paluch
4449
* @author John Blum
50+
* @author Chaelin Kwon
4551
* @since 2.0
4652
*/
4753
public class RedisCacheConfiguration {
@@ -120,6 +126,106 @@ public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader c
120126
conversionService);
121127
}
122128

129+
private static final Logger logger = Logger.getLogger(RedisCacheConfiguration.class.getName());
130+
131+
public static RedisCacheConfiguration propertyCacheConfig(@Nullable ClassLoader classLoader) {
132+
133+
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
134+
registerDefaultConverters(conversionService);
135+
136+
Properties properties = loadProperties();
137+
138+
Duration ttl = getValidatedDuration(properties, "ttl", Duration.ofSeconds(0));
139+
boolean cacheNullValues = getValidatedBoolean(properties, "nullValues", true);
140+
String keyPrefix = getValidatedString(properties, "keyPrefix", "simple", new String[]{"simple", "none"});
141+
SerializationPair<?> defaultSerializer = getValidatedSerializer(properties, "serializer", "raw", classLoader);
142+
SerializationPair<String> keySerializer = getValidatedSerializer(properties, "key.serializer", "string", classLoader);
143+
SerializationPair<?> valueSerializer = getValidatedSerializer(properties, "value.serializer", defaultSerializer, classLoader);
144+
145+
return new RedisCacheConfiguration(
146+
TtlFunction.just(ttl),
147+
cacheNullValues,
148+
DEFAULT_ENABLE_TIME_TO_IDLE_EXPIRATION,
149+
DEFAULT_USE_PREFIX,
150+
CacheKeyPrefix.prefixed(keyPrefix),
151+
keySerializer,
152+
valueSerializer,
153+
conversionService
154+
);
155+
}
156+
157+
private static Properties loadProperties() {
158+
Properties properties = new Properties();
159+
try (InputStream input = RedisCacheConfiguration.class.getClassLoader().getResourceAsStream("redis-cache.properties")) {
160+
if (input != null) {
161+
properties.load(input);
162+
} else {
163+
throw new FileNotFoundException("Property file 'redis-cache.properties' not found in the classpath");
164+
}
165+
} catch (IOException e) {
166+
throw new RuntimeException("Failed to load redis-cache.properties", e);
167+
}
168+
return properties;
169+
}
170+
171+
private static Duration getValidatedDuration(Properties properties, String key, Duration defaultValue) {
172+
String value = properties.getProperty(key, String.valueOf(defaultValue.getSeconds()));
173+
try {
174+
return Duration.ofSeconds(Long.parseLong(value));
175+
} catch (NumberFormatException e) {
176+
logger.warning("Invalid " + key + " value: " + value + ". Expected a positive integer. Defaulting to " + defaultValue);
177+
return defaultValue;
178+
}
179+
}
180+
181+
private static boolean getValidatedBoolean(Properties properties, String key, boolean defaultValue) {
182+
String value = properties.getProperty(key, String.valueOf(defaultValue));
183+
switch (value.toLowerCase()) {
184+
case "true":
185+
case "false":
186+
return Boolean.parseBoolean(value);
187+
default:
188+
logger.warning("Invalid " + key + " value: " + value + ". Expected 'true' or 'false'. Defaulting to " + defaultValue);
189+
return defaultValue;
190+
}
191+
}
192+
193+
private static String getValidatedString(Properties properties, String key, String defaultValue, String[] validValues) {
194+
String value = properties.getProperty(key, defaultValue);
195+
for (String validValue : validValues) {
196+
if (validValue.equalsIgnoreCase(value)) {
197+
return value.toLowerCase();
198+
}
199+
}
200+
logger.warning("Invalid " + key + " value: " + value + ". Expected one of " + String.join(", ", validValues) + ". Defaulting to " + defaultValue);
201+
return defaultValue;
202+
}
203+
204+
private static <T> SerializationPair<T> getValidatedSerializer(Properties properties, String key, Object defaultValue, @Nullable ClassLoader classLoader) {
205+
String value = properties.getProperty(key);
206+
207+
if (value == null && defaultValue instanceof SerializationPair) {
208+
return (SerializationPair<T>) defaultValue;
209+
}
210+
if (value == null) {
211+
value = String.valueOf(defaultValue);
212+
}
213+
214+
switch (value.toLowerCase()) {
215+
case "java":
216+
return (SerializationPair<T>) SerializationPair.fromSerializer(RedisSerializer.java(classLoader));
217+
case "string":
218+
return (SerializationPair<T>) SerializationPair.fromSerializer(RedisSerializer.string());
219+
case "json":
220+
return (SerializationPair<T>) SerializationPair.fromSerializer(RedisSerializer.json());
221+
case "raw":
222+
return (SerializationPair<T>) SerializationPair.fromSerializer(RedisSerializer.byteArray());
223+
default:
224+
logger.warning("Invalid " + key + " value: " + value + ". Expected 'raw', 'java', 'string', or 'json'. Defaulting to 'raw'.");
225+
return (SerializationPair<T>) SerializationPair.fromSerializer(RedisSerializer.byteArray());
226+
}
227+
}
228+
123229
private final boolean cacheNullValues;
124230
private final boolean enableTimeToIdle;
125231
private final boolean usePrefix;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
ttl=100
2+
nullValues=false
3+
keyPrefix=none
4+
serializer=json
5+
key.serializer=string
6+
value.serializer=json

src/test/java/org/springframework/data/redis/cache/RedisCacheConfigurationUnitTests.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.redis.cache;
1717

1818
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
1920
import static org.mockito.ArgumentMatchers.any;
2021
import static org.mockito.ArgumentMatchers.isNull;
2122
import static org.mockito.Mockito.doReturn;
@@ -24,12 +25,17 @@
2425
import static org.mockito.Mockito.verify;
2526
import static org.mockito.Mockito.verifyNoMoreInteractions;
2627

28+
import java.io.FileNotFoundException;
29+
import java.io.IOException;
30+
import java.io.InputStream;
2731
import java.time.Duration;
32+
import java.util.Properties;
2833

2934
import org.junit.jupiter.api.Test;
3035

3136
import org.springframework.beans.DirectFieldAccessor;
3237
import org.springframework.core.convert.converter.Converter;
38+
import org.springframework.data.redis.serializer.RedisSerializer;
3339
import org.springframework.instrument.classloading.ShadowingClassLoader;
3440
import org.springframework.lang.Nullable;
3541

@@ -38,6 +44,7 @@
3844
*
3945
* @author Mark Paluch
4046
* @author John Blum
47+
* @author Chaelin Kwon
4148
*/
4249
class RedisCacheConfigurationUnitTests {
4350

@@ -128,4 +135,65 @@ public String convert(DomainType source) {
128135
return null;
129136
}
130137
}
138+
139+
@Test // DATAREDIS-938
140+
void getCacheConfigFromProperties() {
141+
142+
RedisCacheConfiguration config = RedisCacheConfiguration.propertyCacheConfig(null);
143+
144+
Properties properties = new Properties();
145+
try (InputStream input = RedisCacheConfiguration.class.getClassLoader().getResourceAsStream("redis-cache.properties")) {
146+
if (input != null) {
147+
properties.load(input);
148+
} else {
149+
throw new FileNotFoundException("Property file 'redis-cache.properties' not found in the classpath");
150+
}
151+
} catch (IOException e) {
152+
throw new RuntimeException("Failed to load redis-cache.properties", e);
153+
}
154+
155+
assertThat(config.getTtlFunction().getTimeToLive(null, null)).isEqualTo(Duration.ofSeconds(Long.parseLong(properties.getProperty("ttl", "0"))));
156+
assertThat(config.usePrefix()).isTrue();
157+
assertThat(config.getKeyPrefixFor("myCache")).isEqualTo(properties.getProperty("keyPrefix", "") + "myCache::");
158+
assertThat(config.getAllowCacheNullValues()).isEqualTo(Boolean.parseBoolean(properties.getProperty("nullValues", "true")));
159+
}
160+
161+
@Test // DATAREDIS-938
162+
void testPropertiesFileNonExist() {
163+
assertThatThrownBy(() -> {
164+
try (InputStream input = RedisCacheConfiguration.class.getClassLoader().getResourceAsStream("nonexistent-file.properties")) {
165+
if (input == null) {
166+
throw new FileNotFoundException("Property file 'nonexistent-file.properties' not found in the classpath");
167+
}
168+
Properties properties = new Properties();
169+
properties.load(input);
170+
} catch (Exception e) {
171+
throw new RuntimeException("Failed to load properties", e);
172+
}
173+
}).isInstanceOf(RuntimeException.class)
174+
.hasMessageContaining("Failed to load properties");
175+
}
176+
177+
@Test // DATAREDIS-938
178+
void getInvalidFromProperties() {
179+
180+
RedisCacheConfiguration config = RedisCacheConfiguration.propertyCacheConfig(null);
181+
182+
Properties properties = new Properties();
183+
try (InputStream input = RedisCacheConfiguration.class.getClassLoader().getResourceAsStream("redis-cache.properties")) {
184+
if (input != null) {
185+
properties.load(input);
186+
} else {
187+
throw new FileNotFoundException("Property file 'redis-cache.properties' not found in the classpath");
188+
}
189+
} catch (IOException e) {
190+
throw new RuntimeException("Failed to load redis-cache.properties", e);
191+
}
192+
193+
assertThat(config.getTtlFunction().getTimeToLive(null, null)).isEqualTo(Duration.ZERO); //
194+
assertThat(config.usePrefix()).isTrue();
195+
assertThat(config.getKeyPrefixFor("myCache")).isEqualTo(properties.getProperty("keyPrefix", "") + "myCache::");
196+
assertThat(config.getAllowCacheNullValues()).isEqualTo(Boolean.parseBoolean(properties.getProperty("nullValues", "true")));
197+
}
198+
131199
}

0 commit comments

Comments
 (0)