mirror of
https://github.com/LamGC/ContentGrabbingJi.git
synced 2025-04-29 22:27:33 +00:00
[Add][Change] Common 更改 CacheStoreBuilder 为实例模式, 为 CacheStoreFactory 添加 initial 方法;
[Add] CacheStoreFactory 添加 'initial(File)' 方法供缓存组件进行初始化; [Change] CacheStoreBuilder 将 CacheStoreBuilder 从静态类更改为实例类, 增加对 CacheStoreFactory 的初始化; [Change] CacheStoreBuilderTest 适配 CacheStoreBuilder 的更改;
This commit is contained in:
parent
235c7452b8
commit
b7d712da21
@ -20,6 +20,8 @@ package net.lamgc.cgj.bot.cache;
|
||||
import net.lamgc.cgj.bot.cache.convert.StringConverter;
|
||||
import net.lamgc.cgj.bot.cache.exception.GetCacheStoreException;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 缓存存储容器构造工厂.
|
||||
*
|
||||
@ -28,6 +30,15 @@ import net.lamgc.cgj.bot.cache.exception.GetCacheStoreException;
|
||||
*/
|
||||
public interface CacheStoreFactory {
|
||||
|
||||
/**
|
||||
* Factory 初始化方法.
|
||||
* <p> 当 Factory 被加载且验证正确后, 将会在第一次获取 CacheStore 前被调用一次(也可能在验证完成后执行).
|
||||
* <p> 该方法仅调用一次, 之后将不再调用.
|
||||
* @param dataDirectory 数据存储目录, 可在该目录内存储配置, 或持久化缓存.
|
||||
* @throws Exception 上层管理类允许捕获任何抛出的异常, 如果 Factory 抛出异常, 将视为失效, 不被使用.
|
||||
*/
|
||||
void initial(File dataDirectory) throws Exception;
|
||||
|
||||
/**
|
||||
* 获取一个新的 CacheStore 对象.
|
||||
* @param identify 缓存标识.
|
||||
|
@ -17,11 +17,14 @@
|
||||
|
||||
package net.lamgc.cgj.bot.cache;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import net.lamgc.cgj.bot.cache.convert.StringConverter;
|
||||
import net.lamgc.cgj.bot.cache.exception.GetCacheStoreException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
@ -37,18 +40,36 @@ import java.util.function.Function;
|
||||
public class CacheStoreBuilder {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(CacheStoreBuilder.class);
|
||||
private final static List<CacheStoreFactory> FACTORY_LIST = new ArrayList<>();
|
||||
private final static Map<CacheStoreFactory, FactoryInfo> FACTORY_INFO_MAP = new Hashtable<>();
|
||||
|
||||
private final List<CacheStoreFactory> FACTORY_LIST = new ArrayList<>();
|
||||
private final Map<CacheStoreFactory, FactoryInfo> FACTORY_INFO_MAP = new Hashtable<>();
|
||||
private final File dataDirectory;
|
||||
|
||||
public static CacheStoreBuilder getInstance(File cacheDataDirectory) throws IOException {
|
||||
if (!cacheDataDirectory.exists() && !cacheDataDirectory.mkdirs()) {
|
||||
throw new IOException("Data directory creation failed: " + cacheDataDirectory.getAbsolutePath());
|
||||
} else if (!cacheDataDirectory.isDirectory()) {
|
||||
throw new IOException("The specified data store path is not a directory: " +
|
||||
cacheDataDirectory.getAbsolutePath());
|
||||
} else if (!cacheDataDirectory.canRead() || !cacheDataDirectory.canWrite()) {
|
||||
throw new IOException("The specified data store directory cannot be read or written.");
|
||||
}
|
||||
return new CacheStoreBuilder(cacheDataDirectory);
|
||||
}
|
||||
|
||||
private CacheStoreBuilder(File dataDirectory) {
|
||||
this.dataDirectory = dataDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 SPI 机制加载所有缓存组件.
|
||||
*
|
||||
* <p>第一次执行时加载, 由 {@link #getFactory(CacheStoreSource, Function)} 调用.
|
||||
* <p>由于 ServiceLoader 线程不安全, 所以通过 synchronized 保证其安全性.
|
||||
* 不通过 static 块进行初始化的原因是因为担心发生异常导致无法继续执行
|
||||
* (除非必要, 否则不要使用 static 执行可能会发生异常的代码.).
|
||||
* 不通过 块进行初始化的原因是因为担心发生异常导致无法继续执行
|
||||
* (除非必要, 否则不要使用 执行可能会发生异常的代码.).
|
||||
*/
|
||||
private synchronized static void loadFactory() {
|
||||
private synchronized void loadFactory() {
|
||||
if (FACTORY_LIST.size() != 0) {
|
||||
return;
|
||||
}
|
||||
@ -67,6 +88,12 @@ public class CacheStoreBuilder {
|
||||
log.warn("Factory {} 加载失败: {}", factory.getClass().getName(), e.getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!initialFactory(factory, info)) {
|
||||
log.warn("Factory {} 初始化失败.", info.getFactoryName());
|
||||
continue;
|
||||
}
|
||||
FACTORY_LIST.add(factory);
|
||||
log.info("Factory {} 已加载(优先级: {}, 实现类: {}).",
|
||||
info.getFactoryName(),
|
||||
@ -81,10 +108,26 @@ public class CacheStoreBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean initialFactory(CacheStoreFactory factory, FactoryInfo info) {
|
||||
File factoryDataDirectory = new File(dataDirectory, info.getFactoryName());
|
||||
if (!factoryDataDirectory.exists() && !factoryDataDirectory.mkdirs()) {
|
||||
log.warn("Factory {} 数据存储目录创建失败, 可能会影响后续操作. (Path: {})",
|
||||
info.getFactoryName(),
|
||||
factoryDataDirectory.getAbsolutePath());
|
||||
}
|
||||
try {
|
||||
factory.initial(factoryDataDirectory);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("Factory {} 初始化失败.\n{}", info.getFactoryName(), Throwables.getStackTraceAsString(e));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 优先级排序器.
|
||||
*/
|
||||
private final static class PriorityComparator implements Comparator<CacheStoreFactory> {
|
||||
private final class PriorityComparator implements Comparator<CacheStoreFactory> {
|
||||
@Override
|
||||
public int compare(CacheStoreFactory o1, CacheStoreFactory o2) {
|
||||
FactoryInfo info1 = Objects.requireNonNull(FACTORY_INFO_MAP.get(o1));
|
||||
@ -97,7 +140,7 @@ public class CacheStoreBuilder {
|
||||
* 获取一个当前可用的高优先级 Factory 对象.
|
||||
* @return 返回可用的高优先级 Factory 对象.
|
||||
*/
|
||||
private static <R extends CacheStore<?>> R getFactory(CacheStoreSource storeSource,
|
||||
private <R extends CacheStore<?>> R getFactory(CacheStoreSource storeSource,
|
||||
Function<CacheStoreFactory, R> function)
|
||||
throws NoSuchFactoryException {
|
||||
if (FACTORY_LIST.size() == 0) {
|
||||
@ -156,7 +199,7 @@ public class CacheStoreBuilder {
|
||||
* @return 返回新的存储容器, 与其他容器互不干扰.
|
||||
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
|
||||
*/
|
||||
public static <V> SingleCacheStore<V> newSingleCacheStore(String identify, StringConverter<V> converter) {
|
||||
public <V> SingleCacheStore<V> newSingleCacheStore(String identify, StringConverter<V> converter) {
|
||||
return newSingleCacheStore(null, identify, converter);
|
||||
}
|
||||
|
||||
@ -169,7 +212,7 @@ public class CacheStoreBuilder {
|
||||
* @return 返回新的存储容器, 与其他容器互不干扰.
|
||||
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
|
||||
*/
|
||||
public static <V> SingleCacheStore<V> newSingleCacheStore(CacheStoreSource storeSource, String identify,
|
||||
public <V> SingleCacheStore<V> newSingleCacheStore(CacheStoreSource storeSource, String identify,
|
||||
StringConverter<V> converter) {
|
||||
try {
|
||||
return getFactory(storeSource, factory -> {
|
||||
@ -192,7 +235,7 @@ public class CacheStoreBuilder {
|
||||
* @return 返回新的存储容器, 与其他容器互不干扰.
|
||||
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
|
||||
*/
|
||||
public static <E> ListCacheStore<E> newListCacheStore(String identify, StringConverter<E> converter) {
|
||||
public <E> ListCacheStore<E> newListCacheStore(String identify, StringConverter<E> converter) {
|
||||
return newListCacheStore(null, identify, converter);
|
||||
}
|
||||
|
||||
@ -205,7 +248,7 @@ public class CacheStoreBuilder {
|
||||
* @return 返回新的存储容器, 与其他容器互不干扰.
|
||||
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
|
||||
*/
|
||||
public static <E> ListCacheStore<E> newListCacheStore(CacheStoreSource storeSource, String identify,
|
||||
public <E> ListCacheStore<E> newListCacheStore(CacheStoreSource storeSource, String identify,
|
||||
StringConverter<E> converter) {
|
||||
try {
|
||||
return getFactory(storeSource, factory -> {
|
||||
@ -228,7 +271,7 @@ public class CacheStoreBuilder {
|
||||
* @return 返回新的存储容器, 与其他容器互不干扰.
|
||||
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
|
||||
*/
|
||||
public static <E> SetCacheStore<E> newSetCacheStore(String identify, StringConverter<E> converter) {
|
||||
public <E> SetCacheStore<E> newSetCacheStore(String identify, StringConverter<E> converter) {
|
||||
return newSetCacheStore(null, identify, converter);
|
||||
}
|
||||
|
||||
@ -240,7 +283,7 @@ public class CacheStoreBuilder {
|
||||
* @return 返回新的存储容器, 与其他容器互不干扰.
|
||||
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
|
||||
*/
|
||||
public static <E> SetCacheStore<E> newSetCacheStore(CacheStoreSource storeSource, String identify,
|
||||
public <E> SetCacheStore<E> newSetCacheStore(CacheStoreSource storeSource, String identify,
|
||||
StringConverter<E> converter) {
|
||||
try {
|
||||
return getFactory(storeSource, factory -> {
|
||||
@ -263,7 +306,7 @@ public class CacheStoreBuilder {
|
||||
* @return 返回新的存储容器, 与其他容器互不干扰.
|
||||
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
|
||||
*/
|
||||
public static <V> MapCacheStore<V> newMapCacheStore(String identify, StringConverter<V> converter) {
|
||||
public <V> MapCacheStore<V> newMapCacheStore(String identify, StringConverter<V> converter) {
|
||||
return newMapCacheStore(null, identify, converter);
|
||||
}
|
||||
|
||||
@ -276,7 +319,7 @@ public class CacheStoreBuilder {
|
||||
* @return 返回新的存储容器, 与其他容器互不干扰.
|
||||
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
|
||||
*/
|
||||
public static <V> MapCacheStore<V> newMapCacheStore(CacheStoreSource storeSource, String identify,
|
||||
public <V> MapCacheStore<V> newMapCacheStore(CacheStoreSource storeSource, String identify,
|
||||
StringConverter<V> converter) {
|
||||
try {
|
||||
return getFactory(storeSource, factory -> {
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
package net.lamgc.cgj.bot.cache;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import net.lamgc.cgj.bot.cache.convert.StringConverter;
|
||||
import net.lamgc.cgj.bot.cache.convert.StringToStringConverter;
|
||||
import net.lamgc.cgj.bot.cache.local.CopyOnWriteArrayListCacheStore;
|
||||
@ -24,31 +25,54 @@ import net.lamgc.cgj.bot.cache.local.HashSetCacheStore;
|
||||
import net.lamgc.cgj.bot.cache.redis.RedisMapCacheStore;
|
||||
import net.lamgc.cgj.bot.cache.redis.RedisSingleCacheStore;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @see CacheStoreBuilder
|
||||
*/
|
||||
public class CacheStoreBuilderTest {
|
||||
|
||||
private final static TemporaryFolder tempDirectory = TemporaryFolder.builder().build();
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeAction() {
|
||||
try {
|
||||
tempDirectory.create();
|
||||
} catch (IOException e) {
|
||||
Assert.fail(Throwables.getStackTraceAsString(e));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCacheStoreTest() {
|
||||
final String identify = "test";
|
||||
final StringConverter<String> converter = new StringToStringConverter();
|
||||
CacheStoreBuilder cacheStoreBuilder;
|
||||
try {
|
||||
cacheStoreBuilder = CacheStoreBuilder.getInstance(tempDirectory.getRoot());
|
||||
} catch (IOException e) {
|
||||
Assert.fail(Throwables.getStackTraceAsString(e));
|
||||
return;
|
||||
}
|
||||
|
||||
SingleCacheStore<String> singleCacheStore = CacheStoreBuilder.newSingleCacheStore(CacheStoreSource.REMOTE, identify, converter);
|
||||
SingleCacheStore<String> singleCacheStore = cacheStoreBuilder.newSingleCacheStore(CacheStoreSource.REMOTE, identify, converter);
|
||||
Assert.assertNotNull(singleCacheStore);
|
||||
Assert.assertEquals(RedisSingleCacheStore.class, singleCacheStore.getClass());
|
||||
|
||||
ListCacheStore<String> listCacheStore = CacheStoreBuilder.newListCacheStore(CacheStoreSource.MEMORY, identify, converter);
|
||||
ListCacheStore<String> listCacheStore = cacheStoreBuilder.newListCacheStore(CacheStoreSource.MEMORY, identify, converter);
|
||||
Assert.assertNotNull(listCacheStore);
|
||||
Assert.assertEquals(CopyOnWriteArrayListCacheStore.class, listCacheStore.getClass());
|
||||
|
||||
MapCacheStore<String> mapCacheStore = CacheStoreBuilder.newMapCacheStore(CacheStoreSource.REMOTE, identify, converter);
|
||||
MapCacheStore<String> mapCacheStore = cacheStoreBuilder.newMapCacheStore(CacheStoreSource.REMOTE, identify, converter);
|
||||
Assert.assertNotNull(mapCacheStore);
|
||||
Assert.assertEquals(RedisMapCacheStore.class, mapCacheStore.getClass());
|
||||
|
||||
SetCacheStore<String> setCacheStore = CacheStoreBuilder.newSetCacheStore(identify, converter);
|
||||
SetCacheStore<String> setCacheStore = cacheStoreBuilder.newSetCacheStore(identify, converter);
|
||||
Assert.assertNotNull(setCacheStore);
|
||||
Assert.assertEquals(HashSetCacheStore.class, setCacheStore.getClass());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user