mirror of
				https://github.com/LamGC/ContentGrabbingJi.git
				synced 2025-11-04 10:36:57 +00:00 
			
		
		
		
	[Add][Update] CacheStore-redis 添加 Redis 缓存存储容器的实现模块;
[Add] META-INF/services/* 支持 SPI 机制; [Add] RedisCacheStore, RedisCacheStoreFactory, RedisConnectionPool, RedisUtils 添加相关父类和辅助类; [Add] RedisMapCacheStore, RedisMapCacheStoreTest 添加映射表缓存相关实现及完整单元测试; [Add] RedisSingleCacheStore, RedisSingleCacheStoreTest 添加单项缓存相关实现及完整单元测试; [Update] redis.clients:jedis 更新依赖项版本(3.2.0 -> 3.3.0);
This commit is contained in:
		@ -38,7 +38,7 @@
 | 
				
			|||||||
        <dependency>
 | 
					        <dependency>
 | 
				
			||||||
            <groupId>redis.clients</groupId>
 | 
					            <groupId>redis.clients</groupId>
 | 
				
			||||||
            <artifactId>jedis</artifactId>
 | 
					            <artifactId>jedis</artifactId>
 | 
				
			||||||
            <version>3.2.0</version>
 | 
					            <version>3.3.0</version>
 | 
				
			||||||
        </dependency>
 | 
					        </dependency>
 | 
				
			||||||
    </dependencies>
 | 
					    </dependencies>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										102
									
								
								ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisCacheStore.java
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisCacheStore.java
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,102 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020  LamGC
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as
 | 
				
			||||||
 | 
					 * published by the Free Software Foundation, either version 3 of the
 | 
				
			||||||
 | 
					 * License, or (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package net.lamgc.cgj.bot.cache.redis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.CacheKey;
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.CacheStore;
 | 
				
			||||||
 | 
					import redis.clients.jedis.Jedis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author LamGC
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class RedisCacheStore<V> implements CacheStore<V> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取 Key 前缀.
 | 
				
			||||||
 | 
					     * <p>key = getKeyPrefix() + key
 | 
				
			||||||
 | 
					     * @param cacheKey CacheKey 对象.
 | 
				
			||||||
 | 
					     * @return 返回 Key 前缀.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected String getKeyString(CacheKey cacheKey) {
 | 
				
			||||||
 | 
					        return getKeyPrefix() + cacheKey.join(RedisUtils.KEY_SEPARATOR);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取 Key 的完整前缀.
 | 
				
			||||||
 | 
					     * @return 返回完整前缀.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected abstract String getKeyPrefix();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean setTimeToLive(CacheKey key, long ttl) {
 | 
				
			||||||
 | 
					        String keyString = getKeyString(key);
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis -> {
 | 
				
			||||||
 | 
					            Long result;
 | 
				
			||||||
 | 
					            if (ttl >= 0) {
 | 
				
			||||||
 | 
					                result = jedis.pexpire(keyString, ttl);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                result = jedis.persist(keyString);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return result == RedisUtils.RETURN_CODE_OK;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public long getTimeToLive(CacheKey key) {
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis -> {
 | 
				
			||||||
 | 
					            Long ttl = jedis.pttl(getKeyString(key));
 | 
				
			||||||
 | 
					            return ttl < 0 ? -1 : ttl;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public long size() {
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(Jedis::dbSize);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean clear() {
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis -> RedisUtils.isOk(jedis.flushDB()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean exists(CacheKey key) {
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis -> jedis.exists(getKeyString(key)));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean remove(CacheKey key) {
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis -> jedis.del(getKeyString(key)) == RedisUtils.RETURN_CODE_OK);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public Set<String> keySet() {
 | 
				
			||||||
 | 
					        Set<String> keys = RedisConnectionPool.executeRedis(jedis -> jedis.keys(RedisUtils.KEY_PATTERN_ALL));
 | 
				
			||||||
 | 
					        final int prefixLength = getKeyPrefix().length();
 | 
				
			||||||
 | 
					        Set<String> newKeys = new HashSet<>();
 | 
				
			||||||
 | 
					        for (String key : keys) {
 | 
				
			||||||
 | 
					            newKeys.add(key.substring(prefixLength));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return newKeys;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020  LamGC
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as
 | 
				
			||||||
 | 
					 * published by the Free Software Foundation, either version 3 of the
 | 
				
			||||||
 | 
					 * License, or (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package net.lamgc.cgj.bot.cache.redis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.*;
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.convert.StringConverter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author LamGC
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Factory(name = "Redis")
 | 
				
			||||||
 | 
					public class RedisCacheStoreFactory implements CacheStoreFactory {
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public <V> SingleCacheStore<V> newSingleCacheStore(String identify, StringConverter<V> converter) {
 | 
				
			||||||
 | 
					        return new RedisSingleCacheStore<>(identify, converter);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public <E> ListCacheStore<E> newListCacheStore(String identify, StringConverter<E> converter) {
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public <E> SetCacheStore<E> newSetCacheStore(String identify, StringConverter<E> converter) {
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public <V> MapCacheStore<V> newMapCacheStore(String identify, StringConverter<V> converter) {
 | 
				
			||||||
 | 
					        return new RedisMapCacheStore<>(identify, converter);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean canGetCacheStore() {
 | 
				
			||||||
 | 
					        return RedisConnectionPool.available();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,103 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020  LamGC
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as
 | 
				
			||||||
 | 
					 * published by the Free Software Foundation, either version 3 of the
 | 
				
			||||||
 | 
					 * License, or (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package net.lamgc.cgj.bot.cache.redis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					import redis.clients.jedis.Jedis;
 | 
				
			||||||
 | 
					import redis.clients.jedis.JedisPool;
 | 
				
			||||||
 | 
					import redis.clients.jedis.JedisPoolConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicReference;
 | 
				
			||||||
 | 
					import java.util.function.Function;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 统一的 Redis 连接池.
 | 
				
			||||||
 | 
					 * @author LamGC
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class RedisConnectionPool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final static Logger log = LoggerFactory.getLogger(RedisConnectionPool.class);
 | 
				
			||||||
 | 
					    private final static AtomicReference<JedisPool> POOL = new AtomicReference<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static synchronized void reconnectRedis() {
 | 
				
			||||||
 | 
					        JedisPool jedisPool = POOL.get();
 | 
				
			||||||
 | 
					        if (jedisPool != null && !jedisPool.isClosed()) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        JedisPoolConfig config = new JedisPoolConfig();
 | 
				
			||||||
 | 
					        config.setTestOnBorrow(true);
 | 
				
			||||||
 | 
					        config.setTestOnReturn(true);
 | 
				
			||||||
 | 
					        jedisPool = new JedisPool(config);
 | 
				
			||||||
 | 
					        POOL.set(jedisPool);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取一个 Jedis 对象.
 | 
				
			||||||
 | 
					     * <p>注意, 需回收 Jedis 对象, 否则可能会耗尽连接池导致后续操作受到影响.
 | 
				
			||||||
 | 
					     * @return 返回可用的 Jedis 连接.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static Jedis getConnection() {
 | 
				
			||||||
 | 
					        JedisPool pool = POOL.get();
 | 
				
			||||||
 | 
					        if (pool == null || pool.isClosed()) {
 | 
				
			||||||
 | 
					            reconnectRedis();
 | 
				
			||||||
 | 
					            pool = POOL.get();
 | 
				
			||||||
 | 
					            if (pool == null) {
 | 
				
			||||||
 | 
					                throw new IllegalStateException("Redis connection lost");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return pool.getResource();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 执行 Redis 操作并返回相关值.
 | 
				
			||||||
 | 
					     * <p>本方法会自动回收 Jedis.
 | 
				
			||||||
 | 
					     * @param function 待运行的操作.
 | 
				
			||||||
 | 
					     * @param <R> 返回值类型.
 | 
				
			||||||
 | 
					     * @return 返回 function 返回的内容.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static <R> R executeRedis(Function<Jedis, R> function) {
 | 
				
			||||||
 | 
					        try (Jedis jedis = getConnection()) {
 | 
				
			||||||
 | 
					            return function.apply(jedis);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 检查 Redis 连接池是否有可用的资源.
 | 
				
			||||||
 | 
					     * @return 如果连接池依然活跃, 返回 true.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static boolean available() {
 | 
				
			||||||
 | 
					        JedisPool jedisPool = POOL.get();
 | 
				
			||||||
 | 
					        if (jedisPool == null || jedisPool.isClosed()) {
 | 
				
			||||||
 | 
					            reconnectRedis();
 | 
				
			||||||
 | 
					            if (jedisPool == null || jedisPool.isClosed()) {
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (jedisPool.getNumIdle() == 0) {
 | 
				
			||||||
 | 
					            try (Jedis jedis = jedisPool.getResource()) {
 | 
				
			||||||
 | 
					                return "pong".equalsIgnoreCase(jedis.ping());
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                log.error("Redis 连接测试时发生异常", e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,175 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020  LamGC
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as
 | 
				
			||||||
 | 
					 * published by the Free Software Foundation, either version 3 of the
 | 
				
			||||||
 | 
					 * License, or (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package net.lamgc.cgj.bot.cache.redis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.common.base.Strings;
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.CacheKey;
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.MapCacheStore;
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.convert.StringConverter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Redis Map缓存存储容器.
 | 
				
			||||||
 | 
					 * @param <V> 值类型.
 | 
				
			||||||
 | 
					 * @author LamGC
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class RedisMapCacheStore<V> extends RedisCacheStore<Map<String, V>> implements MapCacheStore<V> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final String keyPrefix;
 | 
				
			||||||
 | 
					    private final StringConverter<V> converter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public RedisMapCacheStore(String keyPrefix, StringConverter<V> converter) {
 | 
				
			||||||
 | 
					        keyPrefix = Strings.nullToEmpty(keyPrefix).trim();
 | 
				
			||||||
 | 
					        if (!keyPrefix.isEmpty() && keyPrefix.endsWith(RedisUtils.KEY_SEPARATOR)) {
 | 
				
			||||||
 | 
					            this.keyPrefix = keyPrefix;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            this.keyPrefix = keyPrefix + RedisUtils.KEY_SEPARATOR;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.converter = Objects.requireNonNull(converter);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected String getKeyPrefix() {
 | 
				
			||||||
 | 
					        return this.keyPrefix;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int mapSize(CacheKey key) {
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis -> {
 | 
				
			||||||
 | 
					            String keyString = getKeyString(key);
 | 
				
			||||||
 | 
					            if (jedis.exists(keyString)) {
 | 
				
			||||||
 | 
					                return jedis.hlen(keyString).intValue();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public Set<String> mapFieldSet(CacheKey key) {
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis -> {
 | 
				
			||||||
 | 
					            String keyString = getKeyString(key);
 | 
				
			||||||
 | 
					            if (jedis.exists(keyString)) {
 | 
				
			||||||
 | 
					                return jedis.hkeys(keyString);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public Set<V> mapValueSet(CacheKey key) {
 | 
				
			||||||
 | 
					        List<String> rawValueSet = RedisConnectionPool.executeRedis(jedis -> {
 | 
				
			||||||
 | 
					            String keyString = getKeyString(key);
 | 
				
			||||||
 | 
					            if (jedis.exists(keyString)) {
 | 
				
			||||||
 | 
					                return jedis.hvals(keyString);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (rawValueSet == null) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Set<V> result = new HashSet<>();
 | 
				
			||||||
 | 
					        for (String rawValue : rawValueSet) {
 | 
				
			||||||
 | 
					            result.add(converter.from(rawValue));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean put(CacheKey key, String field, V value) {
 | 
				
			||||||
 | 
					        Objects.requireNonNull(field);
 | 
				
			||||||
 | 
					        Objects.requireNonNull(value);
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis -> {
 | 
				
			||||||
 | 
					            String keyString = getKeyString(key);
 | 
				
			||||||
 | 
					            return jedis.hset(keyString, field, converter.to(value)) == RedisUtils.RETURN_CODE_OK;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean putAll(CacheKey key, Map<String, V> map) {
 | 
				
			||||||
 | 
					        Objects.requireNonNull(key);
 | 
				
			||||||
 | 
					        Objects.requireNonNull(map);
 | 
				
			||||||
 | 
					        if (map.size() == 0 && exists(key)) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final Map<String, String> targetMap = new HashMap<>(map.size());
 | 
				
			||||||
 | 
					        map.forEach((k, v) -> targetMap.put(k, converter.to(v)));
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis -> {
 | 
				
			||||||
 | 
					            String keyString = getKeyString(key);
 | 
				
			||||||
 | 
					            return RedisUtils.isOk(jedis.hmset(keyString, targetMap));
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean putIfNotExist(CacheKey key, String field, V value) {
 | 
				
			||||||
 | 
					        Objects.requireNonNull(field);
 | 
				
			||||||
 | 
					        Objects.requireNonNull(value);
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis -> {
 | 
				
			||||||
 | 
					            String keyString = getKeyString(key);
 | 
				
			||||||
 | 
					            return jedis.hsetnx(keyString, field, converter.to(value)) == RedisUtils.RETURN_CODE_OK;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public V get(CacheKey key, String field) {
 | 
				
			||||||
 | 
					        Objects.requireNonNull(field);
 | 
				
			||||||
 | 
					        String value = RedisConnectionPool.executeRedis(jedis -> jedis.hget(getKeyString(key), field));
 | 
				
			||||||
 | 
					        if (value == null) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return converter.from(value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean removeField(CacheKey key, String field) {
 | 
				
			||||||
 | 
					        Objects.requireNonNull(field);
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis ->
 | 
				
			||||||
 | 
					                jedis.hdel(getKeyString(key), field) == RedisUtils.RETURN_CODE_OK);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean containsField(CacheKey key, String field) {
 | 
				
			||||||
 | 
					        Objects.requireNonNull(field);
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis -> jedis.hexists(getKeyString(key), field));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean mapIsEmpty(CacheKey key) {
 | 
				
			||||||
 | 
					        return mapSize(key) == 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean clearMap(CacheKey key) {
 | 
				
			||||||
 | 
					        Set<String> fields = mapFieldSet(key);
 | 
				
			||||||
 | 
					        if (fields == null) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        } else if (fields.size() == 0) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String[] fieldsArray = new String[fields.size()];
 | 
				
			||||||
 | 
					        fields.toArray(fieldsArray);
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis ->
 | 
				
			||||||
 | 
					                jedis.hdel(getKeyString(key), fieldsArray) != RedisUtils.RETURN_CODE_FAILED);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,77 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020  LamGC
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as
 | 
				
			||||||
 | 
					 * published by the Free Software Foundation, either version 3 of the
 | 
				
			||||||
 | 
					 * License, or (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package net.lamgc.cgj.bot.cache.redis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.common.base.Strings;
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.CacheKey;
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.SingleCacheStore;
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.convert.StringConverter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Objects;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Redis 单项缓存存储容器.
 | 
				
			||||||
 | 
					 * @param <V> 值类型.
 | 
				
			||||||
 | 
					 * @see net.lamgc.cgj.bot.cache.CacheStore
 | 
				
			||||||
 | 
					 * @see net.lamgc.cgj.bot.cache.SingleCacheStore
 | 
				
			||||||
 | 
					 * @see net.lamgc.cgj.bot.cache.redis.RedisCacheStore
 | 
				
			||||||
 | 
					 * @author LamGC
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class RedisSingleCacheStore<V> extends RedisCacheStore<V> implements SingleCacheStore<V> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final String keyPrefix;
 | 
				
			||||||
 | 
					    private final StringConverter<V> converter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public RedisSingleCacheStore(String keyPrefix, StringConverter<V> converter) {
 | 
				
			||||||
 | 
					        keyPrefix = Strings.nullToEmpty(keyPrefix).trim();
 | 
				
			||||||
 | 
					        if (!keyPrefix.isEmpty() && keyPrefix.endsWith(RedisUtils.KEY_SEPARATOR)) {
 | 
				
			||||||
 | 
					            this.keyPrefix = keyPrefix;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            this.keyPrefix = keyPrefix + RedisUtils.KEY_SEPARATOR;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.converter = Objects.requireNonNull(converter);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean set(CacheKey key, V value) {
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis ->
 | 
				
			||||||
 | 
					                RedisUtils.isOk(jedis.set(getKeyString(key), converter.to(Objects.requireNonNull(value)))));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean setIfNotExist(CacheKey key, V value) {
 | 
				
			||||||
 | 
					        return RedisConnectionPool.executeRedis(jedis ->
 | 
				
			||||||
 | 
					                jedis.setnx(getKeyString(key), converter.to(Objects.requireNonNull(value)))
 | 
				
			||||||
 | 
					                        == RedisUtils.RETURN_CODE_OK);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public V get(CacheKey key) {
 | 
				
			||||||
 | 
					        String value = RedisConnectionPool.executeRedis(jedis -> jedis.get(getKeyString(key)));
 | 
				
			||||||
 | 
					        if (value == null) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return converter.from(value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected String getKeyPrefix() {
 | 
				
			||||||
 | 
					        return this.keyPrefix;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										54
									
								
								ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisUtils.java
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								ContentGrabbingJi-CacheStore-redis/src/main/java/net/lamgc/cgj/bot/cache/redis/RedisUtils.java
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020  LamGC
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as
 | 
				
			||||||
 | 
					 * published by the Free Software Foundation, either version 3 of the
 | 
				
			||||||
 | 
					 * License, or (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package net.lamgc.cgj.bot.cache.redis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author LamGC
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class RedisUtils {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 返回码 - 成功
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public final static int RETURN_CODE_OK = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 返回码 - 失败
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public final static int RETURN_CODE_FAILED = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Key匹配规则 - 所有Key
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public final static String KEY_PATTERN_ALL = "*";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Key 分隔符
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public final static String KEY_SEPARATOR = ":";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 检查字符串返回结果是否为操作成功.
 | 
				
			||||||
 | 
					     * @param result 字符串返回结果.
 | 
				
			||||||
 | 
					     * @return 如果为操作成功, 返回 true.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static boolean isOk(String result) {
 | 
				
			||||||
 | 
					        return "OK".equalsIgnoreCase(result);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright (C) 2020  LamGC
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# ContentGrabbingJi is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					# it under the terms of the GNU Affero General Public License as
 | 
				
			||||||
 | 
					# published by the Free Software Foundation, either version 3 of the
 | 
				
			||||||
 | 
					# License, or (at your option) any later version.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# ContentGrabbingJi is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					# GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					net.lamgc.cgj.bot.cache.redis.RedisCacheStoreFactory
 | 
				
			||||||
@ -0,0 +1,151 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020  LamGC
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as
 | 
				
			||||||
 | 
					 * published by the Free Software Foundation, either version 3 of the
 | 
				
			||||||
 | 
					 * License, or (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package net.lamgc.cgj.bot.cache.redis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.CacheKey;
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.MapCacheStore;
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.convert.StringToStringConverter;
 | 
				
			||||||
 | 
					import org.junit.After;
 | 
				
			||||||
 | 
					import org.junit.Assert;
 | 
				
			||||||
 | 
					import org.junit.Before;
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @see RedisMapCacheStore
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class RedisMapCacheStoreTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final static MapCacheStore<String> cacheStore =
 | 
				
			||||||
 | 
					            new RedisMapCacheStore<>("test", new StringToStringConverter());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Before
 | 
				
			||||||
 | 
					    public void before() {
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.clear());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @After
 | 
				
			||||||
 | 
					    public void after() {
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.clear());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void nullThrowTest() {
 | 
				
			||||||
 | 
					        final CacheKey key = new CacheKey("testKey");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.mapSize(null));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.mapFieldSet(null));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.mapValueSet(null));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.put(null, "field", "value"));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.put(key, null, "value"));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.put(key, "field", null));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.putAll(null, new HashMap<>()));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.putAll(key, null));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.putIfNotExist(null, "field", "value"));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.putIfNotExist(key, null, "value"));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.putIfNotExist(key, "field", null));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.get(key, null));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.get(null, "field"));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.removeField(key, null));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.removeField(null, "field"));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.mapIsEmpty(null));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> cacheStore.clearMap(null));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void keyNotExistTest() {
 | 
				
			||||||
 | 
					        final CacheKey key = new CacheKey("testKey");
 | 
				
			||||||
 | 
					        HashMap<String, String> testMap = new HashMap<>();
 | 
				
			||||||
 | 
					        testMap.put("testField", "value");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 例外情况: 因为 Redis 对空 Map 的处理机制, 导致返回只能为空.
 | 
				
			||||||
 | 
					        Assert.assertEquals(0, cacheStore.mapSize(key));
 | 
				
			||||||
 | 
					        // 例外情况: mapIsEmpty 在 Redis 没有相应指令, 所以是依靠 mapSize 实现的,
 | 
				
			||||||
 | 
					        //     同样因 mapSize 的原因, 不存在等于空.
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.mapIsEmpty(key));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Assert.assertFalse(cacheStore.clearMap(key));
 | 
				
			||||||
 | 
					        Assert.assertFalse(cacheStore.containsField(key, "Field"));
 | 
				
			||||||
 | 
					        Assert.assertFalse(cacheStore.removeField(key, "Field"));
 | 
				
			||||||
 | 
					        Assert.assertNull(cacheStore.get(key, "Field"));
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.put(key, "Field", "value"));
 | 
				
			||||||
 | 
					        Assert.assertTrue("clearMap operation failed!", cacheStore.remove(key));
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.putAll(key, testMap));
 | 
				
			||||||
 | 
					        Assert.assertTrue("clearMap operation failed!", cacheStore.remove(key));
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.putIfNotExist(key, "Field", "value"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void putAndGetTest() {
 | 
				
			||||||
 | 
					        final CacheKey key = new CacheKey("testKey");
 | 
				
			||||||
 | 
					        final Map<String, String> expectedMap = new HashMap<>();
 | 
				
			||||||
 | 
					        expectedMap.put("test01", "testValue01");
 | 
				
			||||||
 | 
					        expectedMap.put("test02", "testValue02");
 | 
				
			||||||
 | 
					        expectedMap.put("test03", "testValue03");
 | 
				
			||||||
 | 
					        expectedMap.put("test04", "testValue04");
 | 
				
			||||||
 | 
					        expectedMap.put("test05", "testValue05");
 | 
				
			||||||
 | 
					        expectedMap.put("test06", "testValue06");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // put/get, mapIsEmpty, containsField
 | 
				
			||||||
 | 
					        Assert.assertTrue("put operation failed!", cacheStore.put(key, "test00", "testValue00"));
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.containsField(key, "test00"));
 | 
				
			||||||
 | 
					        Assert.assertEquals("testValue00", cacheStore.get(key, "test00"));
 | 
				
			||||||
 | 
					        Assert.assertTrue("removeField operation failed!", cacheStore.removeField(key, "test00"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // putIfNotExist
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.putIfNotExist(key, "test00", "testValue00"));
 | 
				
			||||||
 | 
					        Assert.assertFalse(cacheStore.putIfNotExist(key, "test00", "testValue00"));
 | 
				
			||||||
 | 
					        Assert.assertTrue("clearMap operation failed!", cacheStore.clearMap(key));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // putAll
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.putAll(key, expectedMap));
 | 
				
			||||||
 | 
					        Assert.assertTrue(expectedMap.keySet().containsAll(cacheStore.mapFieldSet(key)));
 | 
				
			||||||
 | 
					        Assert.assertTrue(expectedMap.values().containsAll(cacheStore.mapValueSet(key)));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void fieldChangeTest() {
 | 
				
			||||||
 | 
					        final CacheKey key = new CacheKey("testKey");
 | 
				
			||||||
 | 
					        final Map<String, String> expectedMap = new HashMap<>();
 | 
				
			||||||
 | 
					        expectedMap.put("test01", "testValue01");
 | 
				
			||||||
 | 
					        expectedMap.put("test02", "testValue02");
 | 
				
			||||||
 | 
					        expectedMap.put("test03", "testValue03");
 | 
				
			||||||
 | 
					        expectedMap.put("test04", "testValue04");
 | 
				
			||||||
 | 
					        expectedMap.put("test05", "testValue05");
 | 
				
			||||||
 | 
					        expectedMap.put("test06", "testValue06");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // mapSize, clearMap, mapIsEmpty 测试
 | 
				
			||||||
 | 
					        Assert.assertTrue("putAll operation failed!", cacheStore.putAll(key, expectedMap));
 | 
				
			||||||
 | 
					        Assert.assertEquals(expectedMap.size(), cacheStore.mapSize(key));
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.clearMap(key));
 | 
				
			||||||
 | 
					        Assert.assertEquals(0, cacheStore.mapSize(key));
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.mapIsEmpty(key));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // removeField 多分支测试
 | 
				
			||||||
 | 
					        Assert.assertTrue("put operation failed!", cacheStore.put(key, "test00", "testValue00"));
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.containsField(key, "test00"));
 | 
				
			||||||
 | 
					        Assert.assertEquals("testValue00", cacheStore.get(key, "test00"));
 | 
				
			||||||
 | 
					        Assert.assertTrue("removeField operation failed!", cacheStore.removeField(key, "test00"));
 | 
				
			||||||
 | 
					        Assert.assertFalse(cacheStore.removeField(key, "test00"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,149 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020  LamGC
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as
 | 
				
			||||||
 | 
					 * published by the Free Software Foundation, either version 3 of the
 | 
				
			||||||
 | 
					 * License, or (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ContentGrabbingJi is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package net.lamgc.cgj.bot.cache.redis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.CacheKey;
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.SingleCacheStore;
 | 
				
			||||||
 | 
					import net.lamgc.cgj.bot.cache.convert.StringToStringConverter;
 | 
				
			||||||
 | 
					import org.junit.Assert;
 | 
				
			||||||
 | 
					import org.junit.Before;
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @see RedisCacheStore
 | 
				
			||||||
 | 
					 * @see RedisSingleCacheStore
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class RedisSingleCacheStoreTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final static SingleCacheStore<String> cacheStore = new RedisSingleCacheStore<>("test", new StringToStringConverter());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Before
 | 
				
			||||||
 | 
					    public void before() {
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.clear());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void nullThrowTest() {
 | 
				
			||||||
 | 
					        final SingleCacheStore<String> tempCacheStore =
 | 
				
			||||||
 | 
					                new RedisSingleCacheStore<>("test" + RedisUtils.KEY_SEPARATOR, new StringToStringConverter());
 | 
				
			||||||
 | 
					        final CacheKey key = new CacheKey("testKey");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // RedisSingleCacheStore
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.set(null, "testValue"));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.set(key, null));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.get(null));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.setIfNotExist(null, "testValue"));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.setIfNotExist(key, null));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // RedisCacheStore
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.exists(null));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.getTimeToLive(null));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.setTimeToLive(null, 0));
 | 
				
			||||||
 | 
					        Assert.assertThrows(NullPointerException.class, () -> tempCacheStore.remove(null));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void setAndGetTest() {
 | 
				
			||||||
 | 
					        final CacheKey key = new CacheKey("testKey");
 | 
				
			||||||
 | 
					        final String value = "testValue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Assert.assertTrue("Set operation failed!", cacheStore.set(key, value));
 | 
				
			||||||
 | 
					        Assert.assertEquals(value, cacheStore.get(key));
 | 
				
			||||||
 | 
					        Assert.assertTrue("Remove operation failed!", cacheStore.remove(key));
 | 
				
			||||||
 | 
					        Assert.assertNull("Set operation failed!", cacheStore.get(key));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void setIfNotExistTest() {
 | 
				
			||||||
 | 
					        final CacheKey key = new CacheKey("testKey");
 | 
				
			||||||
 | 
					        final String value = "testValue";
 | 
				
			||||||
 | 
					        final String value2 = "testValue02";
 | 
				
			||||||
 | 
					        Assert.assertTrue("Set operation failed!", cacheStore.set(key, value));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Assert.assertFalse(cacheStore.setIfNotExist(key, value2));
 | 
				
			||||||
 | 
					        Assert.assertEquals(value, cacheStore.get(key));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void expireTest() throws InterruptedException {
 | 
				
			||||||
 | 
					        final CacheKey key = new CacheKey("testKey");
 | 
				
			||||||
 | 
					        final String value = "testValue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Cache
 | 
				
			||||||
 | 
					        Assert.assertFalse(cacheStore.setTimeToLive(key, 300));
 | 
				
			||||||
 | 
					        Assert.assertEquals(-1, cacheStore.getTimeToLive(key));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TTL 到期被动检查测试: 使用 exists 经 expire 检查失败后返回 false.
 | 
				
			||||||
 | 
					        Assert.assertTrue("Set operation failed!", cacheStore.set(key, value));
 | 
				
			||||||
 | 
					        Assert.assertTrue("SetTTL operation failed!", cacheStore.setTimeToLive(key, 200));
 | 
				
			||||||
 | 
					        Assert.assertNotEquals(-1, cacheStore.getTimeToLive(key));
 | 
				
			||||||
 | 
					        Thread.sleep(300);
 | 
				
			||||||
 | 
					        Assert.assertFalse(cacheStore.exists(key));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 取消 TTL 测试
 | 
				
			||||||
 | 
					        Assert.assertTrue("Set operation failed!", cacheStore.set(key, value));
 | 
				
			||||||
 | 
					        Assert.assertTrue("SetTTL operation failed!", cacheStore.setTimeToLive(key, 200));
 | 
				
			||||||
 | 
					        Assert.assertTrue("SetTTL operation failed!", cacheStore.setTimeToLive(key, -1));
 | 
				
			||||||
 | 
					        Thread.sleep(300);
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.exists(key));
 | 
				
			||||||
 | 
					        Assert.assertEquals(-1, cacheStore.getTimeToLive(key));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void removeTest() {
 | 
				
			||||||
 | 
					        final CacheKey key = new CacheKey("testKey");
 | 
				
			||||||
 | 
					        final String value = "testValue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 删除不存在Cache测试
 | 
				
			||||||
 | 
					        Assert.assertFalse(cacheStore.remove(key));
 | 
				
			||||||
 | 
					        // 删除存在的Cache测试
 | 
				
			||||||
 | 
					        Assert.assertTrue("Set operation failed!", cacheStore.set(key, value));
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.remove(key));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void clearTest() {
 | 
				
			||||||
 | 
					        final CacheKey key = new CacheKey("testKey");
 | 
				
			||||||
 | 
					        final String value = "testValue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Assert.assertTrue("Set operation failed!", cacheStore.set(key, value));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Assert.assertTrue(cacheStore.exists(key));
 | 
				
			||||||
 | 
					        Assert.assertTrue("Clear operation failed!", cacheStore.clear());
 | 
				
			||||||
 | 
					        Assert.assertFalse(cacheStore.exists(key));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void sizeAndKeySetTest() {
 | 
				
			||||||
 | 
					        Map<String, String> expectedMap = new HashMap<>();
 | 
				
			||||||
 | 
					        expectedMap.put("test01", "testValue01");
 | 
				
			||||||
 | 
					        expectedMap.put("test02", "testValue02");
 | 
				
			||||||
 | 
					        expectedMap.put("test03", "testValue03");
 | 
				
			||||||
 | 
					        expectedMap.put("test04", "testValue04");
 | 
				
			||||||
 | 
					        expectedMap.put("test05", "testValue05");
 | 
				
			||||||
 | 
					        expectedMap.put("test06", "testValue06");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expectedMap.forEach((key, value) -> cacheStore.set(new CacheKey(key), value));
 | 
				
			||||||
 | 
					        Assert.assertEquals(expectedMap.size(), cacheStore.size());
 | 
				
			||||||
 | 
					        Assert.assertTrue(expectedMap.keySet().containsAll(cacheStore.keySet()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user