Skip to content

Commit

Permalink
Redis support (#103)
Browse files Browse the repository at this point in the history
* init redis support

* fix readme
  • Loading branch information
ttulka authored Apr 14, 2022
1 parent 30a767f commit 24adbb7
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 7 deletions.
2 changes: 1 addition & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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()"
Expand Down
5 changes: 5 additions & 0 deletions bucket4j-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 {


}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<String, byte[]> redisTemplate;

public RedisCacheResolver(RedisTemplate<String, byte[]> redisTemplate) {
this.redisTemplate = redisTemplate;
}

public ProxyManager<String> resolve(String cacheName) {
return new RedisProxyManager(redisTemplate, cacheName);
}
}
Original file line number Diff line number Diff line change
@@ -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<String> {

private final RedisTemplate<String, byte[]> redisTemplate;
private final String cacheName;

protected RedisProxyManager(RedisTemplate<String, byte[]> redisTemplate, String cacheName) {
super(ClientSideConfig.getDefault());
this.redisTemplate = redisTemplate;
this.cacheName = cacheName;
}

@Override
public <T> CommandResult<T> execute(String key, Request<T> request) {
byte[] resultBytes = new BucketProcessor<T>(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 <T> CompletableFuture<CommandResult<T>> executeAsync(String key, Request<T> request) {
// not supported yet
throw new UnsupportedOperationException();
}

@Override
protected CompletableFuture<Void> removeAsync(String key) {
// not supported yet
throw new UnsupportedOperationException();
}

private String buildRedisKey(String key) {
return cacheName + "." + key;
}

private static class BucketProcessor<T> {

private final RedisTemplate<String, byte[]> redisTemplate;

public BucketProcessor(RedisTemplate<String, byte[]> redisTemplate) {
this.redisTemplate = redisTemplate;
}

public byte[] process(String key, Request<T> request) {
return new RedisTransaction(redisTemplate, key, InternalSerializationHelper.serializeRequest(request)).execute();
}
}

private static class RedisTransaction extends AbstractBinaryTransaction {

private final RedisTemplate<String, byte[]> redisTemplate;
private final String key;

private RedisTransaction(RedisTemplate<String, byte[]> 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);
}
}
}
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,14 @@


<properties>
<revision>0.5.2</revision>
<revision>0.6.0</revision>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<bucket4j.version>7.0.0</bucket4j.version>
<spring.boot.version>2.6.2</spring.boot.version>
<spring.cloud.version>3.1.0</spring.cloud.version>
<spring.redis.version>2.6.3</spring.redis.version>
<micrometer.version>1.8.1</micrometer.version>
</properties>

Expand Down Expand Up @@ -102,6 +103,11 @@
<artifactId>bucket4j-infinispan</artifactId>
<version>${bucket4j.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>${spring.redis.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down

0 comments on commit 24adbb7

Please sign in to comment.