diff --git a/ContentGrabbingJi-CacheStore-api/src/main/java/net/lamgc/cgj/bot/cache/CacheStoreFactory.java b/ContentGrabbingJi-CacheStore-api/src/main/java/net/lamgc/cgj/bot/cache/CacheStoreFactory.java
index a1ec36b..8e6fbb9 100644
--- a/ContentGrabbingJi-CacheStore-api/src/main/java/net/lamgc/cgj/bot/cache/CacheStoreFactory.java
+++ b/ContentGrabbingJi-CacheStore-api/src/main/java/net/lamgc/cgj/bot/cache/CacheStoreFactory.java
@@ -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 初始化方法.
+ *
当 Factory 被加载且验证正确后, 将会在第一次获取 CacheStore 前被调用一次(也可能在验证完成后执行).
+ *
该方法仅调用一次, 之后将不再调用.
+ * @param dataDirectory 数据存储目录, 可在该目录内存储配置, 或持久化缓存.
+ * @throws Exception 上层管理类允许捕获任何抛出的异常, 如果 Factory 抛出异常, 将视为失效, 不被使用.
+ */
+ void initial(File dataDirectory) throws Exception;
+
/**
* 获取一个新的 CacheStore 对象.
* @param identify 缓存标识.
diff --git a/ContentGrabbingJi-common/src/main/java/net/lamgc/cgj/bot/cache/CacheStoreBuilder.java b/ContentGrabbingJi-common/src/main/java/net/lamgc/cgj/bot/cache/CacheStoreBuilder.java
index b081995..b7650a5 100644
--- a/ContentGrabbingJi-common/src/main/java/net/lamgc/cgj/bot/cache/CacheStoreBuilder.java
+++ b/ContentGrabbingJi-common/src/main/java/net/lamgc/cgj/bot/cache/CacheStoreBuilder.java
@@ -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 FACTORY_LIST = new ArrayList<>();
- private final static Map FACTORY_INFO_MAP = new Hashtable<>();
+
+ private final List FACTORY_LIST = new ArrayList<>();
+ private final Map 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 机制加载所有缓存组件.
*
* 第一次执行时加载, 由 {@link #getFactory(CacheStoreSource, Function)} 调用.
*
由于 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 {
+ private final class PriorityComparator implements Comparator {
@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 getFactory(CacheStoreSource storeSource,
+ private > R getFactory(CacheStoreSource storeSource,
Function function)
throws NoSuchFactoryException {
if (FACTORY_LIST.size() == 0) {
@@ -156,7 +199,7 @@ public class CacheStoreBuilder {
* @return 返回新的存储容器, 与其他容器互不干扰.
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
*/
- public static SingleCacheStore newSingleCacheStore(String identify, StringConverter converter) {
+ public SingleCacheStore newSingleCacheStore(String identify, StringConverter converter) {
return newSingleCacheStore(null, identify, converter);
}
@@ -169,7 +212,7 @@ public class CacheStoreBuilder {
* @return 返回新的存储容器, 与其他容器互不干扰.
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
*/
- public static SingleCacheStore newSingleCacheStore(CacheStoreSource storeSource, String identify,
+ public SingleCacheStore newSingleCacheStore(CacheStoreSource storeSource, String identify,
StringConverter converter) {
try {
return getFactory(storeSource, factory -> {
@@ -192,7 +235,7 @@ public class CacheStoreBuilder {
* @return 返回新的存储容器, 与其他容器互不干扰.
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
*/
- public static ListCacheStore newListCacheStore(String identify, StringConverter converter) {
+ public ListCacheStore newListCacheStore(String identify, StringConverter converter) {
return newListCacheStore(null, identify, converter);
}
@@ -205,7 +248,7 @@ public class CacheStoreBuilder {
* @return 返回新的存储容器, 与其他容器互不干扰.
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
*/
- public static ListCacheStore newListCacheStore(CacheStoreSource storeSource, String identify,
+ public ListCacheStore newListCacheStore(CacheStoreSource storeSource, String identify,
StringConverter converter) {
try {
return getFactory(storeSource, factory -> {
@@ -228,7 +271,7 @@ public class CacheStoreBuilder {
* @return 返回新的存储容器, 与其他容器互不干扰.
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
*/
- public static SetCacheStore newSetCacheStore(String identify, StringConverter converter) {
+ public SetCacheStore newSetCacheStore(String identify, StringConverter converter) {
return newSetCacheStore(null, identify, converter);
}
@@ -240,7 +283,7 @@ public class CacheStoreBuilder {
* @return 返回新的存储容器, 与其他容器互不干扰.
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
*/
- public static SetCacheStore newSetCacheStore(CacheStoreSource storeSource, String identify,
+ public SetCacheStore newSetCacheStore(CacheStoreSource storeSource, String identify,
StringConverter converter) {
try {
return getFactory(storeSource, factory -> {
@@ -263,7 +306,7 @@ public class CacheStoreBuilder {
* @return 返回新的存储容器, 与其他容器互不干扰.
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
*/
- public static MapCacheStore newMapCacheStore(String identify, StringConverter converter) {
+ public MapCacheStore newMapCacheStore(String identify, StringConverter converter) {
return newMapCacheStore(null, identify, converter);
}
@@ -276,7 +319,7 @@ public class CacheStoreBuilder {
* @return 返回新的存储容器, 与其他容器互不干扰.
* @throws GetCacheStoreException 当无法获取可用的 CacheStore 时抛出.
*/
- public static MapCacheStore newMapCacheStore(CacheStoreSource storeSource, String identify,
+ public MapCacheStore newMapCacheStore(CacheStoreSource storeSource, String identify,
StringConverter converter) {
try {
return getFactory(storeSource, factory -> {
diff --git a/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/CacheStoreBuilderTest.java b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/CacheStoreBuilderTest.java
index 509efe9..d881b77 100644
--- a/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/CacheStoreBuilderTest.java
+++ b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/CacheStoreBuilderTest.java
@@ -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 converter = new StringToStringConverter();
+ CacheStoreBuilder cacheStoreBuilder;
+ try {
+ cacheStoreBuilder = CacheStoreBuilder.getInstance(tempDirectory.getRoot());
+ } catch (IOException e) {
+ Assert.fail(Throwables.getStackTraceAsString(e));
+ return;
+ }
- SingleCacheStore singleCacheStore = CacheStoreBuilder.newSingleCacheStore(CacheStoreSource.REMOTE, identify, converter);
+ SingleCacheStore singleCacheStore = cacheStoreBuilder.newSingleCacheStore(CacheStoreSource.REMOTE, identify, converter);
Assert.assertNotNull(singleCacheStore);
Assert.assertEquals(RedisSingleCacheStore.class, singleCacheStore.getClass());
- ListCacheStore listCacheStore = CacheStoreBuilder.newListCacheStore(CacheStoreSource.MEMORY, identify, converter);
+ ListCacheStore listCacheStore = cacheStoreBuilder.newListCacheStore(CacheStoreSource.MEMORY, identify, converter);
Assert.assertNotNull(listCacheStore);
Assert.assertEquals(CopyOnWriteArrayListCacheStore.class, listCacheStore.getClass());
- MapCacheStore mapCacheStore = CacheStoreBuilder.newMapCacheStore(CacheStoreSource.REMOTE, identify, converter);
+ MapCacheStore mapCacheStore = cacheStoreBuilder.newMapCacheStore(CacheStoreSource.REMOTE, identify, converter);
Assert.assertNotNull(mapCacheStore);
Assert.assertEquals(RedisMapCacheStore.class, mapCacheStore.getClass());
- SetCacheStore setCacheStore = CacheStoreBuilder.newSetCacheStore(identify, converter);
+ SetCacheStore setCacheStore = cacheStoreBuilder.newSetCacheStore(identify, converter);
Assert.assertNotNull(setCacheStore);
Assert.assertEquals(HashSetCacheStore.class, setCacheStore.getClass());
}