[Change] 调整包路径;

[Add] RedisPoolCacheStore, JsonRedisCacheStore 增加可提供JedisPool对象的构造方法, 以实现Redis连接池共用;
This commit is contained in:
2020-04-10 22:00:38 +08:00
parent 9bf59da444
commit 8375b81b17
8 changed files with 33 additions and 12 deletions

View File

@ -0,0 +1,40 @@
package net.lamgc.cgj.bot.cache;
import java.util.Date;
import java.util.concurrent.atomic.AtomicReference;
public class CacheObject<T> {
private AtomicReference<T> value;
private AtomicReference<Date> expire;
public CacheObject() {
this(null, null);
}
public CacheObject(T value, Date expire) {
this.value = new AtomicReference<>(value);
this.expire = new AtomicReference<>(expire);
}
public synchronized void update(T value, Date newExpire) {
if(new Date().after(newExpire)) {
throw new IllegalArgumentException("Due earlier than current time");
}
this.expire.set(newExpire);
this.value.set(value);
}
public synchronized T get() {
return value.get();
}
public Date getExpireDate() {
return expire.get();
}
public boolean isExpire(Date time) {
Date expireDate = getExpireDate();
return expireDate != null && expireDate.before(time);
}
}

View File

@ -0,0 +1,49 @@
package net.lamgc.cgj.bot.cache;
import java.util.Date;
public interface CacheStore<T> {
/**
* 更新或添加缓存项
* @param key 缓存键名
* @param value 缓存值
* @param expire 过期时间, 如不过期传入null
*/
void update(String key, T value, Date expire);
/**
* 获取缓存数据
* @param key 键名
* @return 如果存在, 返回对象, 不存在或获取失败则返回null
*/
T getCache(String key);
/**
* 如果该键存在且未过期则返回true.
* @param key 要查询的键
* @return 如果存在且未过期则返回true
*/
boolean exists(String key);
/**
* 如果该键存在且未过期则返回true
* @param key 要查询的键
* @param date 检查的时间
* @return 如果存在且未过期则返回true
*/
boolean exists(String key, Date date);
/**
* 清空缓存
* @return 如果清空成功, 返回true
*/
boolean clear();
/**
* 是否支持持久化
* @return 如果支持返回true
*/
boolean supportedPersistence();
}

View File

@ -0,0 +1,79 @@
package net.lamgc.cgj.bot.cache;
import net.lamgc.cgj.Main;
import net.lamgc.cgj.pixiv.PixivURL;
import net.lamgc.utils.event.EventHandler;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class ImageCacheHandler implements EventHandler {
private final static Logger log = LoggerFactory.getLogger("ImageCacheHandler");
private final static HttpClient httpClient = HttpClientBuilder.create().setProxy(Main.proxy).build();
private final static Set<ImageCacheObject> cacheQueue = Collections.synchronizedSet(new HashSet<>());
public void getImageToCache(ImageCacheObject event) {
if(cacheQueue.contains(event)) {
log.info("图片 {} 已存在相同缓存任务, 跳过.", event.getStoreFile().getName());
return;
} else {
cacheQueue.add(event);
}
try {
log.info("图片 {} Event正在进行...({})", event.getStoreFile().getName(), Integer.toHexString(event.hashCode()));
File storeFile = event.getStoreFile();
log.info("正在缓存图片 {} (Path: {})", storeFile.getName(), storeFile.getAbsolutePath());
try {
if(!storeFile.exists() && !storeFile.createNewFile()) {
log.error("无法创建文件(Path: {})", storeFile.getAbsolutePath());
return;
}
} catch (IOException e) {
log.error("无法创建文件(Path: {})", storeFile.getAbsolutePath());
e.printStackTrace();
}
HttpGet request = new HttpGet(event.getDownloadLink());
request.addHeader("Referer", PixivURL.getPixivRefererLink(event.getIllustId()));
HttpResponse response;
try {
response = httpClient.execute(request);
} catch (IOException e) {
log.error("Http请求时发生异常", e);
return;
}
if(response.getStatusLine().getStatusCode() != 200) {
log.warn("Http请求异常{}", response.getStatusLine());
return;
}
log.info("正在下载...(Content-Length: {}KB)", response.getEntity().getContentLength() / 1024);
try(FileOutputStream fos = new FileOutputStream(storeFile)) {
IOUtils.copy(response.getEntity().getContent(), fos);
} catch (IOException e) {
log.error("下载图片时发生异常", e);
return;
}
event.getImageCache().put(event.getDownloadLink(), storeFile);
} finally {
log.info("图片 {} Event结束({})", event.getStoreFile().getName(), Integer.toHexString(event.hashCode()));
cacheQueue.remove(event);
}
}
}

View File

@ -0,0 +1,57 @@
package net.lamgc.cgj.bot.cache;
import net.lamgc.utils.event.EventObject;
import java.io.File;
import java.util.Map;
import java.util.Objects;
public class ImageCacheObject implements EventObject {
private final Map<String, File> imageCache;
private final int illustId;
private final String downloadLink;
private final File storeFile;
public ImageCacheObject(Map<String, File> imageCache, int illustId, String downloadLink, File storeFile) {
this.imageCache = imageCache;
this.illustId = illustId;
this.downloadLink = downloadLink;
this.storeFile = storeFile;
}
public Map<String, File> getImageCache() {
return imageCache;
}
public String getDownloadLink() {
return downloadLink;
}
public File getStoreFile() {
return storeFile;
}
public int getIllustId() {
return illustId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ImageCacheObject that = (ImageCacheObject) o;
return illustId == that.illustId &&
Objects.equals(imageCache, that.imageCache) &&
Objects.equals(downloadLink, that.downloadLink) &&
Objects.equals(storeFile, that.storeFile);
}
@Override
public int hashCode() {
return Objects.hash(imageCache, illustId, downloadLink, storeFile);
}
}

View File

@ -0,0 +1,32 @@
package net.lamgc.cgj.bot.cache;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import redis.clients.jedis.JedisPool;
import java.net.URI;
public class JsonRedisCacheStore extends RedisPoolCacheStore<JsonElement> {
private final Gson gson;
public JsonRedisCacheStore(URI redisServerUri, String prefix, Gson gson) {
super(redisServerUri, prefix);
this.gson = gson;
}
public JsonRedisCacheStore(JedisPool jedisPool, String prefix, Gson gson) {
super(jedisPool, prefix);
this.gson = gson;
}
@Override
protected String parse(JsonElement data) {
return this.gson.toJson(data);
}
@Override
protected JsonElement analysis(String dataStr) {
return this.gson.fromJson(dataStr, JsonElement.class);
}
}

View File

@ -0,0 +1,82 @@
package net.lamgc.cgj.bot.cache;
import java.util.Date;
import java.util.Hashtable;
import java.util.Objects;
public class LocalHashCacheStore<T> implements CacheStore<T> {
private final Hashtable<String, CacheObject<T>> cache;
public LocalHashCacheStore() {
this(0);
}
public LocalHashCacheStore(int initialCapacity) {
this(initialCapacity, 0F);
}
public LocalHashCacheStore(int initialCapacity, float loadFactor) {
if(initialCapacity != 0) {
if(loadFactor <= 0F) {
cache = new Hashtable<>(initialCapacity);
} else {
cache = new Hashtable<>(initialCapacity, loadFactor);
}
} else {
cache = new Hashtable<>();
}
}
@Override
public void update(String key, T value, Date expire) {
if(cache.containsKey(key)) {
cache.get(key).update(value, expire);
} else {
CacheObject<T> cacheObject = new CacheObject<>(value, expire);
cache.put(key, cacheObject);
}
}
@Override
public T getCache(String key) {
if(!cache.containsKey(key)) {
return null;
}
CacheObject<T> cacheObject = cache.get(key);
if(cacheObject.isExpire(new Date())) {
cache.remove(key);
return null;
}
return cacheObject.get();
}
@Override
public boolean exists(String key) {
return exists(key, null);
}
@Override
public boolean exists(String key, Date date) {
if(!cache.containsKey(key)) {
return false;
}
CacheObject<T> cacheObject = cache.get(key);
if(cacheObject.isExpire(Objects.isNull(date) ? new Date() : date)) {
cache.remove(key);
return false;
}
return true;
}
@Override
public boolean clear() {
cache.clear();
return true;
}
@Override
public boolean supportedPersistence() {
return false;
}
}

View File

@ -0,0 +1,100 @@
package net.lamgc.cgj.bot.cache;
import com.google.common.base.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisConnectionException;
import java.net.URI;
import java.util.Date;
public abstract class RedisCacheStore<T> implements CacheStore<T> {
private final Jedis jedis;
private final Logger log;
private final String keyPrefix;
public RedisCacheStore(URI redisServerUri, String prefix) {
this(redisServerUri, null, prefix);
}
/**
* 创建一个Redis缓存数据库对象
* @param redisServerUri 数据库链接
* @param password 登录密码(如果有)
* @throws JedisConnectionException 当连接失败时抛出
*/
public RedisCacheStore(URI redisServerUri, String password, String prefix) throws JedisConnectionException {
this.jedis = new Jedis(redisServerUri.getHost(), redisServerUri.getPort() <= 0 ? 6379 : redisServerUri.getPort());
log = LoggerFactory.getLogger(this.getClass().getSimpleName() + "@" + Integer.toHexString(jedis.hashCode()));
log.info("Redis数据库连接状态: {}", jedis.ping());
if(password != null) {
this.jedis.auth(password);
}
if(!Strings.isNullOrEmpty(prefix)) {
keyPrefix = prefix.endsWith(".") ? prefix : prefix + ".";
} else {
keyPrefix = "";
}
}
public void connect() {
if(!"PONE".equals(jedis.ping())) {
jedis.connect();
}
}
@Override
public void update(String key, T value, Date expire) {
Transaction multi = jedis.multi();
multi.set(keyPrefix + key, parse(value));
if(expire != null) {
multi.expireAt(keyPrefix + key, expire.getTime());
log.debug("已设置Key {} 的过期时间(Expire: {})", key, expire.getTime());
}
multi.exec();
}
@Override
public T getCache(String key) {
return analysis(jedis.get(keyPrefix + key));
}
@Override
public boolean exists(String key) {
return jedis.exists(keyPrefix + key);
}
@Override
public boolean exists(String key, Date date) {
return exists(key);
}
@Override
public boolean clear() {
String result = jedis.flushDB();
log.info("flushDB返回结果: {}", result);
return true;
}
/**
* 转换方法
* @param dataObj 原数据
* @return 文本型数据
*/
protected abstract String parse(T dataObj);
/**
* 将String数据转换成指定类型的对象
* @param dataStr String数据
* @return 泛型指定类型的对象
*/
protected abstract T analysis(String dataStr);
@Override
public boolean supportedPersistence() {
return true;
}
}

View File

@ -0,0 +1,109 @@
package net.lamgc.cgj.bot.cache;
import com.google.common.base.Strings;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.*;
import java.net.URI;
import java.util.Date;
import java.util.Objects;
public abstract class RedisPoolCacheStore<T> implements CacheStore<T> {
private final JedisPool jedisPool;
private final String keyPrefix;
private final Logger log;
public RedisPoolCacheStore(URI redisServerUri, String prefix) {
this(redisServerUri, null, 0, null, prefix);
}
public RedisPoolCacheStore(URI redisServerUri, JedisPoolConfig config, int timeout, String password, String prefix) {
jedisPool = new JedisPool(config == null ? new GenericObjectPoolConfig<JedisPool>() : config, redisServerUri.getHost(),
redisServerUri.getPort() <= 0 ? 6379 : redisServerUri.getPort(),
timeout <= 0 ? Protocol.DEFAULT_TIMEOUT : timeout, password);
log = LoggerFactory.getLogger(this.getClass().getSimpleName() + "@" + Integer.toHexString(jedisPool.hashCode()));
if(prefix != null) {
keyPrefix = prefix.endsWith(".") ? prefix : prefix + ".";
} else {
keyPrefix = "";
}
}
public RedisPoolCacheStore(JedisPool pool, String keyPrefix) {
jedisPool = Objects.requireNonNull(pool);
if(jedisPool.isClosed()) {
throw new IllegalStateException("JedisPool is closed");
}
log = LoggerFactory.getLogger(this.getClass().getSimpleName() + "@" + Integer.toHexString(jedisPool.hashCode()));
if(!Strings.isNullOrEmpty(keyPrefix)) {
this.keyPrefix = keyPrefix.endsWith(".") ? keyPrefix : keyPrefix + ".";
} else {
this.keyPrefix = "";
}
}
@Override
public void update(String key, T value, Date expire) {
Jedis jedis = jedisPool.getResource();
Transaction multi = jedis.multi();
multi.set(keyPrefix + key, parse(value));
if(expire != null) {
multi.expireAt(keyPrefix + key, expire.getTime());
log.debug("已设置Key {} 的过期时间(Expire: {})", key, expire.getTime());
}
multi.exec();
jedis.close();
}
@Override
public T getCache(String key) {
Jedis jedis = jedisPool.getResource();
T result = analysis(jedis.get(keyPrefix + key));
jedis.close();
return result;
}
@Override
public boolean exists(String key) {
Jedis jedis = jedisPool.getResource();
boolean result = jedis.exists(keyPrefix + key);
jedis.close();
return result;
}
@Override
public boolean exists(String key, Date date) {
return exists(key);
}
@Override
public boolean clear() {
Jedis jedis = jedisPool.getResource();
String result = jedis.flushDB();
jedis.close();
log.info("flushDB返回结果: {}", result);
return true;
}
/**
* 转换方法
* @param dataObj 原数据
* @return 文本型数据
*/
protected abstract String parse(T dataObj);
/**
* 将String数据转换成指定类型的对象
* @param dataStr String数据
* @return 泛型指定类型的对象
*/
protected abstract T analysis(String dataStr);
@Override
public boolean supportedPersistence() {
return true;
}
}