diff --git a/README.adoc b/README.adoc
index e45e41d6..6aa9fae6 100644
--- a/README.adoc
+++ b/README.adoc
@@ -230,7 +230,7 @@ bucket4j:
- key: USERNAME
expression: "@securityService.username() != null ? @securityService.username() : 'anonym'"
- key: URL
- expression: request.getRequestURI()
+ expression: getRequestURI()
rate-limits:
- execute-condition: "@securityService.username() == 'admin'"
expression: "@securityService.username()?: getRemoteAddr()"
diff --git a/bucket4j-spring-boot-starter/pom.xml b/bucket4j-spring-boot-starter/pom.xml
index 8d5fcaa2..eed43e03 100644
--- a/bucket4j-spring-boot-starter/pom.xml
+++ b/bucket4j-spring-boot-starter/pom.xml
@@ -111,6 +111,11 @@
javax.servlet-api
provided
+
+ org.springframework.data
+ spring-data-redis
+ provided
+
org.springframework.boot
spring-boot-configuration-processor
diff --git a/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/cache/Bucket4jCacheConfiguration.java b/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/cache/Bucket4jCacheConfiguration.java
index ce42674b..9d1b5c36 100644
--- a/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/cache/Bucket4jCacheConfiguration.java
+++ b/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/cache/Bucket4jCacheConfiguration.java
@@ -1,6 +1,6 @@
package com.giffing.bucket4j.spring.boot.starter.config.cache;
-
+import com.giffing.bucket4j.spring.boot.starter.config.cache.redis.RedisBucket4jConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -11,11 +11,13 @@
import com.giffing.bucket4j.spring.boot.starter.config.cache.jcache.InfinispanJCacheBucket4jConfiguration;
import com.giffing.bucket4j.spring.boot.starter.config.cache.jcache.JCacheBucket4jConfiguration;
-
@Configuration
@AutoConfigureAfter(CacheAutoConfiguration.class)
-@Import(value = {JCacheBucket4jConfiguration.class, InfinispanJCacheBucket4jConfiguration.class, HazelcastBucket4jCacheConfiguration.class})
+@Import(value = {
+ JCacheBucket4jConfiguration.class,
+ InfinispanJCacheBucket4jConfiguration.class,
+ HazelcastBucket4jCacheConfiguration.class,
+ RedisBucket4jConfiguration.class
+})
public class Bucket4jCacheConfiguration {
-
-
}
diff --git a/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/cache/redis/RedisBucket4jConfiguration.java b/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/cache/redis/RedisBucket4jConfiguration.java
new file mode 100644
index 00000000..923fb90e
--- /dev/null
+++ b/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/cache/redis/RedisBucket4jConfiguration.java
@@ -0,0 +1,19 @@
+package com.giffing.bucket4j.spring.boot.starter.config.cache.redis;
+
+import com.giffing.bucket4j.spring.boot.starter.config.cache.SyncCacheResolver;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.core.RedisTemplate;
+
+@Configuration
+@ConditionalOnClass(RedisTemplate.class)
+@ConditionalOnBean(RedisTemplate.class)
+public class RedisBucket4jConfiguration {
+
+ @Bean
+ public SyncCacheResolver bucket4RedisResolver(RedisTemplate redisTemplate) {
+ return new RedisCacheResolver(redisTemplate);
+ }
+}
diff --git a/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/cache/redis/RedisCacheResolver.java b/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/cache/redis/RedisCacheResolver.java
new file mode 100644
index 00000000..328246d1
--- /dev/null
+++ b/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/cache/redis/RedisCacheResolver.java
@@ -0,0 +1,23 @@
+package com.giffing.bucket4j.spring.boot.starter.config.cache.redis;
+
+import com.giffing.bucket4j.spring.boot.starter.config.cache.CacheResolver;
+import com.giffing.bucket4j.spring.boot.starter.config.cache.SyncCacheResolver;
+import io.github.bucket4j.distributed.proxy.ProxyManager;
+import org.springframework.data.redis.core.RedisTemplate;
+
+/**
+ * This class is the Redis implementation of the {@link CacheResolver}.
+ *
+ */
+public class RedisCacheResolver implements SyncCacheResolver {
+
+ private RedisTemplate redisTemplate;
+
+ public RedisCacheResolver(RedisTemplate redisTemplate) {
+ this.redisTemplate = redisTemplate;
+ }
+
+ public ProxyManager resolve(String cacheName) {
+ return new RedisProxyManager(redisTemplate, cacheName);
+ }
+}
diff --git a/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/cache/redis/RedisProxyManager.java b/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/cache/redis/RedisProxyManager.java
new file mode 100644
index 00000000..d41c3627
--- /dev/null
+++ b/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/cache/redis/RedisProxyManager.java
@@ -0,0 +1,98 @@
+package com.giffing.bucket4j.spring.boot.starter.config.cache.redis;
+
+import io.github.bucket4j.distributed.proxy.AbstractProxyManager;
+import io.github.bucket4j.distributed.proxy.ClientSideConfig;
+import io.github.bucket4j.distributed.remote.AbstractBinaryTransaction;
+import io.github.bucket4j.distributed.remote.CommandResult;
+import io.github.bucket4j.distributed.remote.Request;
+import io.github.bucket4j.distributed.serialization.InternalSerializationHelper;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * The extension of Bucket4j library addressed to support Redis.
+ */
+class RedisProxyManager extends AbstractProxyManager {
+
+ private final RedisTemplate redisTemplate;
+ private final String cacheName;
+
+ protected RedisProxyManager(RedisTemplate redisTemplate, String cacheName) {
+ super(ClientSideConfig.getDefault());
+ this.redisTemplate = redisTemplate;
+ this.cacheName = cacheName;
+ }
+
+ @Override
+ public CommandResult execute(String key, Request request) {
+ byte[] resultBytes = new BucketProcessor(redisTemplate).process(buildRedisKey(key), request);
+ return InternalSerializationHelper.deserializeResult(resultBytes, request.getBackwardCompatibilityVersion());
+ }
+
+ @Override
+ public void removeProxy(String key) {
+ redisTemplate.delete(buildRedisKey(key));
+ }
+
+ @Override
+ public boolean isAsyncModeSupported() {
+ return false;
+ }
+
+ @Override
+ public CompletableFuture> executeAsync(String key, Request request) {
+ // not supported yet
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected CompletableFuture removeAsync(String key) {
+ // not supported yet
+ throw new UnsupportedOperationException();
+ }
+
+ private String buildRedisKey(String key) {
+ return cacheName + "." + key;
+ }
+
+ private static class BucketProcessor {
+
+ private final RedisTemplate redisTemplate;
+
+ public BucketProcessor(RedisTemplate redisTemplate) {
+ this.redisTemplate = redisTemplate;
+ }
+
+ public byte[] process(String key, Request request) {
+ return new RedisTransaction(redisTemplate, key, InternalSerializationHelper.serializeRequest(request)).execute();
+ }
+ }
+
+ private static class RedisTransaction extends AbstractBinaryTransaction {
+
+ private final RedisTemplate redisTemplate;
+ private final String key;
+
+ private RedisTransaction(RedisTemplate redisTemplate, String key, byte[] requestBytes) {
+ super(requestBytes);
+ this.redisTemplate = redisTemplate;
+ this.key = key;
+ }
+
+ @Override
+ public boolean exists() {
+ return redisTemplate.hasKey(key);
+ }
+
+ @Override
+ protected byte[] getRawState() {
+ return redisTemplate.opsForValue().get(key);
+ }
+
+ @Override
+ protected void setRawState(byte[] stateBytes) {
+ redisTemplate.opsForValue().set(key, stateBytes);
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index 0b93f0b9..6948d0a8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,13 +40,14 @@
- 0.5.2
+ 0.6.0
UTF-8
UTF-8
1.8
7.0.0
2.6.2
3.1.0
+ 2.6.3
1.8.1
@@ -102,6 +103,11 @@
bucket4j-infinispan
${bucket4j.version}
+
+ org.springframework.data
+ spring-data-redis
+ ${spring.redis.version}
+