factoryLoader = ServiceLoader.load(CacheStoreFactory.class);
private final File dataDirectory;
+ /**
+ * 获取 CacheStoreBuilder 实例.
+ * 该方法仅提供给 ContentGrabbingJiBot 调用, 请勿通过该方法私自创建 CacheStoreBuilder.
+ * @param cacheDataDirectory 缓存组件数据目录的根目录(例如提供 File 为 {@code /data/cache},
+ * 缓存组件名为 {@code example}, 则缓存组件的缓存路径为 {@code /data/cache/example}).
+ * @return 返回新的 CacheStoreBuilder 实例, 各实例之间是没有任何关系的(包括创建的 CacheStore, 除非缓存组件设计错误).
+ * @throws IOException 当路径检查失败时抛出.
+ */
public static CacheStoreBuilder getInstance(File cacheDataDirectory) throws IOException {
+ Objects.requireNonNull(cacheDataDirectory, "Cache component data directory is null");
if (!cacheDataDirectory.exists() && !cacheDataDirectory.mkdirs()) {
throw new IOException("Data directory creation failed: " + cacheDataDirectory.getAbsolutePath());
} else if (!cacheDataDirectory.isDirectory()) {
@@ -59,6 +69,7 @@ public class CacheStoreBuilder {
private CacheStoreBuilder(File dataDirectory) {
this.dataDirectory = dataDirectory;
+ loadFactory();
}
/**
@@ -70,20 +81,18 @@ public class CacheStoreBuilder {
* (除非必要, 否则不要使用 执行可能会发生异常的代码.).
*/
private synchronized void loadFactory() {
- if (FACTORY_LIST.size() != 0) {
- return;
- }
- final ServiceLoader factoryLoader = ServiceLoader.load(CacheStoreFactory.class);
+ factoryLoader.reload();
+ List newFactoryList = new ArrayList<>();
try {
for (CacheStoreFactory factory : factoryLoader) {
FactoryInfo info;
try {
info = new FactoryInfo(factory.getClass());
- if (FACTORY_INFO_MAP.containsValue(info)) {
+ if (factoryInfoMap.containsValue(info)) {
log.warn("发现 Name 重复的 Factory, 已跳过. (被拒绝的实现: {})", factory.getClass().getName());
continue;
}
- FACTORY_INFO_MAP.put(factory, info);
+ factoryInfoMap.put(factory, info);
} catch (IllegalArgumentException e) {
log.warn("Factory {} 加载失败: {}", factory.getClass().getName(), e.getMessage());
continue;
@@ -94,13 +103,15 @@ public class CacheStoreBuilder {
log.warn("Factory {} 初始化失败.", info.getFactoryName());
continue;
}
- FACTORY_LIST.add(factory);
+ newFactoryList.add(factory);
log.info("Factory {} 已加载(优先级: {}, 实现类: {}).",
info.getFactoryName(),
info.getFactoryPriority(),
factory.getClass().getName());
}
- FACTORY_LIST.sort(new PriorityComparator());
+ newFactoryList.sort(new PriorityComparator());
+ factoryList = newFactoryList;
+ optimizeFactoryInfoMap();
} catch (Error error) {
// 防止发生 Error 又不输出到日志导致玄学问题难以排查.
log.error("加载 CacheStoreFactory 时发生严重错误.", error);
@@ -108,6 +119,19 @@ public class CacheStoreBuilder {
}
}
+ /**
+ * 清除无效的 {@link FactoryInfo}
+ */
+ private void optimizeFactoryInfoMap() {
+ factoryInfoMap.keySet().removeIf(factory -> !factoryList.contains(factory));
+ }
+
+ /**
+ * 初始化 Factory.
+ * @param factory Factory 对象.
+ * @param info Factory Info.
+ * @return 初始化成功则返回 {@code true}, 否则返回 {@code false}.
+ */
private boolean initialFactory(CacheStoreFactory factory, FactoryInfo info) {
File factoryDataDirectory = new File(dataDirectory, info.getFactoryName());
if (!factoryDataDirectory.exists() && !factoryDataDirectory.mkdirs()) {
@@ -130,8 +154,8 @@ public class CacheStoreBuilder {
private final class PriorityComparator implements Comparator {
@Override
public int compare(CacheStoreFactory o1, CacheStoreFactory o2) {
- FactoryInfo info1 = Objects.requireNonNull(FACTORY_INFO_MAP.get(o1));
- FactoryInfo info2 = Objects.requireNonNull(FACTORY_INFO_MAP.get(o2));
+ FactoryInfo info1 = Objects.requireNonNull(factoryInfoMap.get(o1));
+ FactoryInfo info2 = Objects.requireNonNull(factoryInfoMap.get(o2));
return info2.getFactoryPriority() - info1.getFactoryPriority();
}
}
@@ -141,15 +165,12 @@ public class CacheStoreBuilder {
* @return 返回可用的高优先级 Factory 对象.
*/
private > R getFactory(CacheStoreSource storeSource,
- Function function)
+ Function function)
throws NoSuchFactoryException {
- if (FACTORY_LIST.size() == 0) {
- loadFactory();
- }
- Iterator iterator = FACTORY_LIST.iterator();
+ Iterator iterator = factoryList.iterator();
while (iterator.hasNext()) {
CacheStoreFactory factory = iterator.next();
- FactoryInfo info = FACTORY_INFO_MAP.get(factory);
+ FactoryInfo info = factoryInfoMap.get(factory);
if (storeSource != null && info.getStoreSource() != storeSource) {
continue;
}
@@ -191,6 +212,21 @@ public class CacheStoreBuilder {
throw new NoSuchFactoryException();
}
+ /**
+ * 检查是否为 {@code null}.
+ * @param cacheStore 缓存库.
+ * @param factory 工厂对象.
+ * @param 缓存库类型.
+ * @return 如果不为 null, 则正常返回.
+ * @throws GetCacheStoreException 当 cacheStore 为 {@code null} 时抛出.
+ */
+ private > V returnRequireNonNull(V cacheStore, CacheStoreFactory factory) {
+ if (cacheStore == null) {
+ throw new GetCacheStoreException("Factory '" + factory.getClass().getName() + "' returned null");
+ }
+ return cacheStore;
+ }
+
/**
* 获取单项缓存存储容器.
* @param identify 缓存容器标识.
@@ -215,13 +251,8 @@ public class CacheStoreBuilder {
public SingleCacheStore newSingleCacheStore(CacheStoreSource storeSource, String identify,
StringConverter converter) {
try {
- return getFactory(storeSource, factory -> {
- SingleCacheStore singleCacheStoreInstance = factory.newSingleCacheStore(identify, converter);
- if (singleCacheStoreInstance == null) {
- throw new GetCacheStoreException("Factory " + factory.getClass().getName() + " 返回 null.");
- }
- return singleCacheStoreInstance;
- });
+ return getFactory(storeSource, factory ->
+ returnRequireNonNull(factory.newSingleCacheStore(identify, converter), factory));
} catch (NoSuchFactoryException e) {
throw new GetCacheStoreException("无可用的 Factory.", e);
}
@@ -251,13 +282,8 @@ public class CacheStoreBuilder {
public ListCacheStore newListCacheStore(CacheStoreSource storeSource, String identify,
StringConverter converter) {
try {
- return getFactory(storeSource, factory -> {
- ListCacheStore listCacheStoreInstance = factory.newListCacheStore(identify, converter);
- if (listCacheStoreInstance == null) {
- throw new GetCacheStoreException("Factory " + factory.getClass().getName() + " 返回 null.");
- }
- return listCacheStoreInstance;
- });
+ return getFactory(storeSource, factory ->
+ returnRequireNonNull(factory.newListCacheStore(identify, converter), factory));
} catch (NoSuchFactoryException e) {
throw new GetCacheStoreException("无可用的 Factory.", e);
}
@@ -286,13 +312,8 @@ public class CacheStoreBuilder {
public SetCacheStore newSetCacheStore(CacheStoreSource storeSource, String identify,
StringConverter converter) {
try {
- return getFactory(storeSource, factory -> {
- SetCacheStore setCacheStoreInstance = factory.newSetCacheStore(identify, converter);
- if (setCacheStoreInstance == null) {
- throw new GetCacheStoreException("Factory " + factory.getClass().getName() + " 返回 null.");
- }
- return setCacheStoreInstance;
- });
+ return getFactory(storeSource, factory ->
+ returnRequireNonNull(factory.newSetCacheStore(identify, converter), factory));
} catch (NoSuchFactoryException e) {
throw new GetCacheStoreException("无可用的 Factory.", e);
}
@@ -322,13 +343,8 @@ public class CacheStoreBuilder {
public MapCacheStore newMapCacheStore(CacheStoreSource storeSource, String identify,
StringConverter converter) {
try {
- return getFactory(storeSource, factory -> {
- MapCacheStore mapCacheStoreInstance = factory.newMapCacheStore(identify, converter);
- if (mapCacheStoreInstance == null) {
- throw new GetCacheStoreException("Factory " + factory.getClass().getName() + " 返回 null.");
- }
- return mapCacheStoreInstance;
- });
+ return getFactory(storeSource, factory ->
+ returnRequireNonNull(factory.newMapCacheStore(identify, converter), factory));
} catch (NoSuchFactoryException e) {
throw new GetCacheStoreException("无可用的 Factory.", e);
}
diff --git a/ContentGrabbingJi-common/src/main/java/net/lamgc/cgj/bot/cache/FactoryInfo.java b/ContentGrabbingJi-common/src/main/java/net/lamgc/cgj/bot/cache/FactoryInfo.java
index a1df59d..09e15b0 100644
--- a/ContentGrabbingJi-common/src/main/java/net/lamgc/cgj/bot/cache/FactoryInfo.java
+++ b/ContentGrabbingJi-common/src/main/java/net/lamgc/cgj/bot/cache/FactoryInfo.java
@@ -25,7 +25,7 @@ import java.util.Objects;
* CacheStoreFactory 的标识信息.
* @author LamGC
*/
-public class FactoryInfo {
+public final class FactoryInfo {
private final String factoryName;
private final int factoryPriority;
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 d881b77..3cbc801 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
@@ -20,17 +20,24 @@ 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;
-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 net.lamgc.cgj.bot.cache.exception.GetCacheStoreException;
+import net.lamgc.cgj.bot.cache.factory.*;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
/**
* @see CacheStoreBuilder
@@ -38,6 +45,7 @@ import java.io.IOException;
public class CacheStoreBuilderTest {
private final static TemporaryFolder tempDirectory = TemporaryFolder.builder().build();
+ private final static Logger log = LoggerFactory.getLogger(CacheStoreBuilderTest.class);
@BeforeClass
public static void beforeAction() {
@@ -62,20 +70,266 @@ public class CacheStoreBuilderTest {
SingleCacheStore singleCacheStore = cacheStoreBuilder.newSingleCacheStore(CacheStoreSource.REMOTE, identify, converter);
Assert.assertNotNull(singleCacheStore);
- Assert.assertEquals(RedisSingleCacheStore.class, singleCacheStore.getClass());
+ Assert.assertEquals(RemoteCacheFactory.RemoteSingleCacheFactory.class, singleCacheStore.getClass());
ListCacheStore listCacheStore = cacheStoreBuilder.newListCacheStore(CacheStoreSource.MEMORY, identify, converter);
Assert.assertNotNull(listCacheStore);
- Assert.assertEquals(CopyOnWriteArrayListCacheStore.class, listCacheStore.getClass());
+ Assert.assertEquals(MemoryFactory.MemoryListCacheStore.class, listCacheStore.getClass());
- MapCacheStore mapCacheStore = cacheStoreBuilder.newMapCacheStore(CacheStoreSource.REMOTE, identify, converter);
+ MapCacheStore mapCacheStore = cacheStoreBuilder.newMapCacheStore(CacheStoreSource.LOCAL, identify, converter);
Assert.assertNotNull(mapCacheStore);
- Assert.assertEquals(RedisMapCacheStore.class, mapCacheStore.getClass());
+ Assert.assertEquals(LocalFactory.LocalMapCacheStore.class, mapCacheStore.getClass());
SetCacheStore setCacheStore = cacheStoreBuilder.newSetCacheStore(identify, converter);
Assert.assertNotNull(setCacheStore);
- Assert.assertEquals(HashSetCacheStore.class, setCacheStore.getClass());
+ Assert.assertEquals(SetCacheStoreFactory.OnlySetCacheStore.class, setCacheStore.getClass());
+ }
+
+ @Test
+ public void loadFailureTest() throws IllegalAccessException, NoSuchFieldException {
+ CacheStoreBuilder cacheStoreBuilder;
+ try {
+ cacheStoreBuilder = CacheStoreBuilder.getInstance(tempDirectory.getRoot());
+ } catch (IOException e) {
+ Assert.fail(Throwables.getStackTraceAsString(e));
+ return;
+ }
+
+ Field factoryListField;
+ factoryListField = CacheStoreBuilder.class.getDeclaredField("factoryList");
+
+
+ factoryListField.setAccessible(true);
+ Object o = factoryListField.get(cacheStoreBuilder);
+ Assert.assertTrue(o instanceof List);
+ @SuppressWarnings("unchecked")
+ List factoryList = (List) o;
+ Set> classSet = new HashSet<>();
+ factoryList.forEach(factory -> classSet.add(factory.getClass()));
+
+ // 重名检查
+ if (classSet.contains(DuplicateNameFactoryA.class) && classSet.contains(DuplicateNameFactoryB.class)) {
+ Assert.fail("There are different factories with the same name");
+ return;
+ }
+
+ if (classSet.contains(NoAnnotationFactory.class)) {
+ Assert.fail("Factory without @Factory added is loaded");
+ return;
+ }
+
+ if (classSet.contains(InitialFailureFactory.class)) {
+ Assert.fail("The factory that failed to initialize was loaded");
+ }
+
+ }
+
+ @Test
+ public void multiThreadReloadTest() throws IOException, NoSuchMethodException, InterruptedException {
+ final String identify = "test";
+ final StringConverter converter = new StringToStringConverter();
+ final CacheStoreBuilder cacheStoreBuilder = CacheStoreBuilder.getInstance(tempDirectory.getRoot());
+
+ final Method loadFactoryMethod = CacheStoreBuilder.class.getDeclaredMethod("loadFactory");
+ loadFactoryMethod.setAccessible(true);
+
+ Thread accessThread = new Thread(() -> {
+ for (int i = 0; i < 100000; i++) {
+ SingleCacheStore singleCacheStore = cacheStoreBuilder.newSingleCacheStore(CacheStoreSource.REMOTE, identify, converter);
+ Assert.assertNotNull(singleCacheStore);
+ Assert.assertEquals(RemoteCacheFactory.RemoteSingleCacheFactory.class, singleCacheStore.getClass());
+
+ ListCacheStore listCacheStore = cacheStoreBuilder.newListCacheStore(CacheStoreSource.MEMORY, identify, converter);
+ Assert.assertNotNull(listCacheStore);
+ Assert.assertEquals(MemoryFactory.MemoryListCacheStore.class, listCacheStore.getClass());
+
+ MapCacheStore mapCacheStore = cacheStoreBuilder.newMapCacheStore(CacheStoreSource.LOCAL, identify, converter);
+ Assert.assertNotNull(mapCacheStore);
+ Assert.assertEquals(LocalFactory.LocalMapCacheStore.class, mapCacheStore.getClass());
+
+ SetCacheStore setCacheStore = cacheStoreBuilder.newSetCacheStore(identify, converter);
+ Assert.assertNotNull(setCacheStore);
+ Assert.assertEquals(SetCacheStoreFactory.OnlySetCacheStore.class, setCacheStore.getClass());
+ }
+ }, "Thread-AccessBuilder");
+ Thread reloadThread = new Thread(() -> {
+ int count = 0;
+ final Random random = new Random();
+ while(count++ < 100000) {
+ if (random.nextInt() % 2 == 0) {
+ try {
+ loadFactoryMethod.invoke(cacheStoreBuilder);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ log.error("重载 Builder 时发生异常.",
+ e instanceof InvocationTargetException ?
+ ((InvocationTargetException) e).getTargetException() :
+ e);
+ }
+ }
+ }
+ }, "Thread-ReloadBuilder");
+
+ accessThread.start();
+ reloadThread.start();
+
+ accessThread.join();
+ reloadThread.join();
+ }
+
+ @Test
+ public void noSpecifiedGetCacheStoreTest() {
+ 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(identify, converter);
+ Assert.assertNotNull(singleCacheStore);
+
+ ListCacheStore listCacheStore = cacheStoreBuilder.newListCacheStore(identify, converter);
+ Assert.assertNotNull(listCacheStore);
+
+ MapCacheStore mapCacheStore = cacheStoreBuilder.newMapCacheStore(identify, converter);
+ Assert.assertNotNull(mapCacheStore);
+
+ SetCacheStore setCacheStore = cacheStoreBuilder.newSetCacheStore(identify, converter);
+ Assert.assertNotNull(setCacheStore);
+ }
+
+ @Test
+ public void noSuchFactoryExceptionThrowTest() throws NoSuchFieldException, IllegalAccessException {
+ 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;
+ }
+
+ Field factoryListField;
+ factoryListField = CacheStoreBuilder.class.getDeclaredField("factoryList");
+
+
+ factoryListField.setAccessible(true);
+ Object o = factoryListField.get(cacheStoreBuilder);
+ Assert.assertTrue(o instanceof List);
+ @SuppressWarnings("unchecked")
+ List factoryList = (List) o;
+ factoryList.clear();
+
+
+ try {
+ cacheStoreBuilder.newSingleCacheStore(identify, converter);
+ } catch (GetCacheStoreException e) {
+ if (!(e.getCause() instanceof NoSuchFactoryException)) {
+ Assert.fail("The exception is not due to NoSuchFactoryException");
+ }
+ }
+
+ try {
+ cacheStoreBuilder.newMapCacheStore(identify, converter);
+ } catch (GetCacheStoreException e) {
+ if (!(e.getCause() instanceof NoSuchFactoryException)) {
+ Assert.fail("The exception is not due to NoSuchFactoryException");
+ }
+ }
+
+ try {
+ cacheStoreBuilder.newListCacheStore(identify, converter);
+ } catch (GetCacheStoreException e) {
+ if (!(e.getCause() instanceof NoSuchFactoryException)) {
+ Assert.fail("The exception is not due to NoSuchFactoryException");
+ }
+ }
+
+ try {
+ cacheStoreBuilder.newSetCacheStore(identify, converter);
+ } catch (GetCacheStoreException e) {
+ if (!(e.getCause() instanceof NoSuchFactoryException)) {
+ Assert.fail("The exception is not due to NoSuchFactoryException");
+ }
+ }
+
+ }
+
+ @Test
+ public void getInstanceFailureTest() throws IOException {
+ Assert.assertThrows(IOException.class, () ->
+ CacheStoreBuilder.getInstance(tempDirectory.newFile("invalid file.bin")));
+
+ File onlyReadableDirectory = tempDirectory.newFile("onlyReadable");
+ // Assert.assertTrue(onlyReadableDirectory.setWritable(false));
+ Assert.assertThrows(IOException.class, () ->
+ CacheStoreBuilder.getInstance(new File(onlyReadableDirectory, "cache")));
+
+ Assert.assertNotNull(
+ CacheStoreBuilder.getInstance(new File(tempDirectory.newFolder("valid directory"), "cache")));
+
+ }
+
+ @Test
+ public void lastFactoryThrowExceptionTest() throws NoSuchFieldException, IllegalAccessException {
+ 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;
+ }
+
+ Field factoryListField;
+ factoryListField = CacheStoreBuilder.class.getDeclaredField("factoryList");
+
+
+ factoryListField.setAccessible(true);
+ Object o = factoryListField.get(cacheStoreBuilder);
+ Assert.assertTrue(o instanceof List);
+ @SuppressWarnings("unchecked")
+ List factoryList = (List) o;
+ factoryList.removeIf(factory -> !(factory instanceof GetCacheStoreExceptionFactory));
+
+ try {
+ cacheStoreBuilder.newSingleCacheStore(identify, converter);
+ } catch (GetCacheStoreException e) {
+ if (!(e.getCause() instanceof NoSuchFactoryException)) {
+ Assert.fail("The exception is not due to NoSuchFactoryException");
+ }
+ }
+
+ try {
+ cacheStoreBuilder.newMapCacheStore(identify, converter);
+ } catch (GetCacheStoreException e) {
+ if (!(e.getCause() instanceof NoSuchFactoryException)) {
+ Assert.fail("The exception is not due to NoSuchFactoryException");
+ }
+ }
+
+ try {
+ cacheStoreBuilder.newListCacheStore(identify, converter);
+ } catch (GetCacheStoreException e) {
+ if (!(e.getCause() instanceof NoSuchFactoryException)) {
+ Assert.fail("The exception is not due to NoSuchFactoryException");
+ }
+ }
+
+ try {
+ cacheStoreBuilder.newSetCacheStore(identify, converter);
+ } catch (GetCacheStoreException e) {
+ if (!(e.getCause() instanceof NoSuchFactoryException)) {
+ Assert.fail("The exception is not due to NoSuchFactoryException");
+ }
+ }
}
+
}
diff --git a/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/AvailabilityCheckExceptionThrowFactory.java b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/AvailabilityCheckExceptionThrowFactory.java
new file mode 100644
index 0000000..a3aa930
--- /dev/null
+++ b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/AvailabilityCheckExceptionThrowFactory.java
@@ -0,0 +1,57 @@
+/*
+ * 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 .
+ */
+
+package net.lamgc.cgj.bot.cache.factory;
+
+import 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;
+
+@Factory(name = "test-throwException")
+public class AvailabilityCheckExceptionThrowFactory implements CacheStoreFactory {
+ @Override
+ public void initial(File dataDirectory) {
+
+ }
+
+ @Override
+ public SingleCacheStore newSingleCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public ListCacheStore newListCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public SetCacheStore newSetCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public MapCacheStore newMapCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public boolean canGetCacheStore() {
+ throw new IllegalStateException();
+ }
+}
diff --git a/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/DuplicateNameFactoryA.java b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/DuplicateNameFactoryA.java
new file mode 100644
index 0000000..1f20052
--- /dev/null
+++ b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/DuplicateNameFactoryA.java
@@ -0,0 +1,57 @@
+/*
+ * 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 .
+ */
+
+package net.lamgc.cgj.bot.cache.factory;
+
+import 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;
+
+@Factory(name = "duplicate")
+public class DuplicateNameFactoryA implements CacheStoreFactory {
+ @Override
+ public void initial(File dataDirectory) {
+
+ }
+
+ @Override
+ public SingleCacheStore newSingleCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public ListCacheStore newListCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public SetCacheStore newSetCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public MapCacheStore newMapCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public boolean canGetCacheStore() {
+ return false;
+ }
+}
diff --git a/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/DuplicateNameFactoryB.java b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/DuplicateNameFactoryB.java
new file mode 100644
index 0000000..fbcf93d
--- /dev/null
+++ b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/DuplicateNameFactoryB.java
@@ -0,0 +1,57 @@
+/*
+ * 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 .
+ */
+
+package net.lamgc.cgj.bot.cache.factory;
+
+import 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;
+
+@Factory(name = "duplicate", priority = FactoryPriority.PRIORITY_HIGHEST)
+public class DuplicateNameFactoryB implements CacheStoreFactory {
+ @Override
+ public void initial(File dataDirectory) {
+
+ }
+
+ @Override
+ public SingleCacheStore newSingleCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public ListCacheStore newListCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public SetCacheStore newSetCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public MapCacheStore newMapCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public boolean canGetCacheStore() {
+ return false;
+ }
+}
diff --git a/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/GetCacheStoreExceptionFactory.java b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/GetCacheStoreExceptionFactory.java
new file mode 100644
index 0000000..bf94b2c
--- /dev/null
+++ b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/GetCacheStoreExceptionFactory.java
@@ -0,0 +1,57 @@
+/*
+ * 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 .
+ */
+
+package net.lamgc.cgj.bot.cache.factory;
+
+import 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;
+
+@Factory(name = "test-getCacheStoreException", priority = FactoryPriority.PRIORITY_HIGHEST)
+public class GetCacheStoreExceptionFactory implements CacheStoreFactory {
+ @Override
+ public void initial(File dataDirectory) {
+
+ }
+
+ @Override
+ public SingleCacheStore newSingleCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public ListCacheStore newListCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public SetCacheStore newSetCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public MapCacheStore newMapCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public boolean canGetCacheStore() {
+ return true;
+ }
+}
diff --git a/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/InitialFailureFactory.java b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/InitialFailureFactory.java
new file mode 100644
index 0000000..de5458c
--- /dev/null
+++ b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/InitialFailureFactory.java
@@ -0,0 +1,57 @@
+/*
+ * 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 .
+ */
+
+package net.lamgc.cgj.bot.cache.factory;
+
+import 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;
+
+@Factory(name = "initialFailure")
+public class InitialFailureFactory implements CacheStoreFactory {
+ @Override
+ public void initial(File dataDirectory) {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public SingleCacheStore newSingleCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public ListCacheStore newListCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public SetCacheStore newSetCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public MapCacheStore newMapCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public boolean canGetCacheStore() {
+ return false;
+ }
+}
diff --git a/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/LocalFactory.java b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/LocalFactory.java
new file mode 100644
index 0000000..af9cf03
--- /dev/null
+++ b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/LocalFactory.java
@@ -0,0 +1,153 @@
+/*
+ * 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 .
+ */
+
+package net.lamgc.cgj.bot.cache.factory;
+
+import net.lamgc.cgj.bot.cache.*;
+import net.lamgc.cgj.bot.cache.convert.StringConverter;
+import net.lamgc.cgj.bot.cache.exception.GetCacheStoreException;
+import org.junit.Assert;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Set;
+
+@Factory(name = "test-local", source = CacheStoreSource.LOCAL)
+public class LocalFactory implements CacheStoreFactory {
+ @Override
+ public void initial(File dataDirectory) {
+ Assert.assertNotNull(dataDirectory);
+ }
+
+ @Override
+ public SingleCacheStore newSingleCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public ListCacheStore newListCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public SetCacheStore newSetCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public MapCacheStore newMapCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return new LocalMapCacheStore<>();
+ }
+
+ @Override
+ public boolean canGetCacheStore() {
+ return true;
+ }
+
+ public static class LocalMapCacheStore implements MapCacheStore {
+ @Override
+ public int mapSize(CacheKey key) {
+ return 0;
+ }
+
+ @Override
+ public Set mapFieldSet(CacheKey key) {
+ return null;
+ }
+
+ @Override
+ public Set mapValueSet(CacheKey key) {
+ return null;
+ }
+
+ @Override
+ public boolean put(CacheKey key, String field, V value) {
+ return false;
+ }
+
+ @Override
+ public boolean putAll(CacheKey key, Map map) {
+ return false;
+ }
+
+ @Override
+ public boolean putIfNotExist(CacheKey key, String field, V value) {
+ return false;
+ }
+
+ @Override
+ public V get(CacheKey key, String field) {
+ return null;
+ }
+
+ @Override
+ public boolean removeField(CacheKey key, String field) {
+ return false;
+ }
+
+ @Override
+ public boolean containsField(CacheKey key, String field) {
+ return false;
+ }
+
+ @Override
+ public boolean mapIsEmpty(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public boolean clearMap(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public boolean setTimeToLive(CacheKey key, long ttl) {
+ return false;
+ }
+
+ @Override
+ public long getTimeToLive(CacheKey key) {
+ return 0;
+ }
+
+ @Override
+ public long size() {
+ return 0;
+ }
+
+ @Override
+ public boolean clear() {
+ return false;
+ }
+
+ @Override
+ public boolean exists(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public boolean remove(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public Set keySet() {
+ return null;
+ }
+ }
+
+}
diff --git a/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/MemoryFactory.java b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/MemoryFactory.java
new file mode 100644
index 0000000..8dff124
--- /dev/null
+++ b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/MemoryFactory.java
@@ -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 .
+ */
+
+package net.lamgc.cgj.bot.cache.factory;
+
+import net.lamgc.cgj.bot.cache.*;
+import net.lamgc.cgj.bot.cache.convert.StringConverter;
+import net.lamgc.cgj.bot.cache.exception.GetCacheStoreException;
+import org.junit.Assert;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+@Factory(name = "test-memory", source = CacheStoreSource.MEMORY)
+public class MemoryFactory implements CacheStoreFactory {
+ @Override
+ public void initial(File dataDirectory) {
+ Assert.assertNotNull(dataDirectory);
+ }
+
+ @Override
+ public SingleCacheStore newSingleCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public ListCacheStore newListCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return new MemoryListCacheStore<>();
+ }
+
+ @Override
+ public SetCacheStore newSetCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public MapCacheStore newMapCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public boolean canGetCacheStore() {
+ return true;
+ }
+
+ public static class MemoryListCacheStore implements ListCacheStore {
+ @Override
+ public E getElement(CacheKey key, int index) {
+ return null;
+ }
+
+ @Override
+ public List getElementsByRange(CacheKey key, int index, int length) {
+ return null;
+ }
+
+ @Override
+ public boolean removeElement(CacheKey key, int index) {
+ return false;
+ }
+
+ @Override
+ public boolean addElement(CacheKey key, E element) {
+ return false;
+ }
+
+ @Override
+ public boolean addElements(CacheKey key, Collection elements) {
+ return false;
+ }
+
+ @Override
+ public boolean containsElement(CacheKey key, E element) {
+ return false;
+ }
+
+ @Override
+ public boolean isEmpty(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public int elementsLength(CacheKey key) {
+ return 0;
+ }
+
+ @Override
+ public boolean clearCollection(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public boolean removeElement(CacheKey key, E element) {
+ return false;
+ }
+
+ @Override
+ public boolean setTimeToLive(CacheKey key, long ttl) {
+ return false;
+ }
+
+ @Override
+ public long getTimeToLive(CacheKey key) {
+ return 0;
+ }
+
+ @Override
+ public long size() {
+ return 0;
+ }
+
+ @Override
+ public boolean clear() {
+ return false;
+ }
+
+ @Override
+ public boolean exists(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public boolean remove(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public Set keySet() {
+ return null;
+ }
+ }
+
+}
diff --git a/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/NoAnnotationFactory.java b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/NoAnnotationFactory.java
new file mode 100644
index 0000000..f4ec985
--- /dev/null
+++ b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/NoAnnotationFactory.java
@@ -0,0 +1,56 @@
+/*
+ * 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 .
+ */
+
+package net.lamgc.cgj.bot.cache.factory;
+
+import 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;
+
+public class NoAnnotationFactory implements CacheStoreFactory {
+ @Override
+ public void initial(File dataDirectory) {
+
+ }
+
+ @Override
+ public SingleCacheStore newSingleCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public ListCacheStore newListCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public SetCacheStore newSetCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public MapCacheStore newMapCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public boolean canGetCacheStore() {
+ return true;
+ }
+}
diff --git a/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/RemoteCacheFactory.java b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/RemoteCacheFactory.java
new file mode 100644
index 0000000..1e42659
--- /dev/null
+++ b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/RemoteCacheFactory.java
@@ -0,0 +1,113 @@
+/*
+ * 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 .
+ */
+
+package net.lamgc.cgj.bot.cache.factory;
+
+import net.lamgc.cgj.bot.cache.*;
+import net.lamgc.cgj.bot.cache.convert.StringConverter;
+import net.lamgc.cgj.bot.cache.exception.GetCacheStoreException;
+import org.junit.Assert;
+
+import java.io.File;
+import java.util.Set;
+
+@Factory(name = "test-remote", source = CacheStoreSource.REMOTE)
+public class RemoteCacheFactory implements CacheStoreFactory {
+ @Override
+ public void initial(File dataDirectory) {
+ Assert.assertNotNull(dataDirectory);
+ }
+
+ @Override
+ public SingleCacheStore newSingleCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return new RemoteSingleCacheFactory<>();
+ }
+
+ @Override
+ public ListCacheStore newListCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public SetCacheStore newSetCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public MapCacheStore newMapCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public boolean canGetCacheStore() {
+ return true;
+ }
+
+ public static class RemoteSingleCacheFactory implements SingleCacheStore {
+
+ @Override
+ public boolean set(CacheKey key, V value) {
+ return false;
+ }
+
+ @Override
+ public boolean setIfNotExist(CacheKey key, V value) {
+ return false;
+ }
+
+ @Override
+ public V get(CacheKey key) {
+ return null;
+ }
+
+ @Override
+ public boolean setTimeToLive(CacheKey key, long ttl) {
+ return false;
+ }
+
+ @Override
+ public long getTimeToLive(CacheKey key) {
+ return 0;
+ }
+
+ @Override
+ public long size() {
+ return 0;
+ }
+
+ @Override
+ public boolean clear() {
+ return false;
+ }
+
+ @Override
+ public boolean exists(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public boolean remove(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public Set keySet() {
+ return null;
+ }
+ }
+
+}
diff --git a/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/ReturnNullFactory.java b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/ReturnNullFactory.java
new file mode 100644
index 0000000..d3950e0
--- /dev/null
+++ b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/ReturnNullFactory.java
@@ -0,0 +1,57 @@
+/*
+ * 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 .
+ */
+
+package net.lamgc.cgj.bot.cache.factory;
+
+import 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;
+
+@Factory(name = "test-returnNull", priority = FactoryPriority.PRIORITY_HIGHEST)
+public class ReturnNullFactory implements CacheStoreFactory {
+ @Override
+ public void initial(File dataDirectory) {
+
+ }
+
+ @Override
+ public SingleCacheStore newSingleCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public ListCacheStore newListCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public SetCacheStore newSetCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public MapCacheStore newMapCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public boolean canGetCacheStore() {
+ return true;
+ }
+}
diff --git a/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/SetCacheStoreFactory.java b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/SetCacheStoreFactory.java
new file mode 100644
index 0000000..06b3a9a
--- /dev/null
+++ b/ContentGrabbingJi-common/src/test/java/net/lamgc/cgj/bot/cache/factory/SetCacheStoreFactory.java
@@ -0,0 +1,131 @@
+/*
+ * 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 .
+ */
+
+package net.lamgc.cgj.bot.cache.factory;
+
+import 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;
+import java.util.Collection;
+import java.util.Set;
+
+@Factory(name = "test-onlySet")
+public class SetCacheStoreFactory implements CacheStoreFactory {
+ @Override
+ public void initial(File dataDirectory) {
+
+ }
+
+ @Override
+ public SingleCacheStore newSingleCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public ListCacheStore newListCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public SetCacheStore newSetCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return new OnlySetCacheStore<>();
+ }
+
+ @Override
+ public MapCacheStore newMapCacheStore(String identify, StringConverter converter) throws GetCacheStoreException {
+ return null;
+ }
+
+ @Override
+ public boolean canGetCacheStore() {
+ return true;
+ }
+
+ public static class OnlySetCacheStore implements SetCacheStore {
+ @Override
+ public boolean addElement(CacheKey key, E element) {
+ return false;
+ }
+
+ @Override
+ public boolean addElements(CacheKey key, Collection elements) {
+ return false;
+ }
+
+ @Override
+ public boolean containsElement(CacheKey key, E element) {
+ return false;
+ }
+
+ @Override
+ public boolean isEmpty(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public int elementsLength(CacheKey key) {
+ return 0;
+ }
+
+ @Override
+ public boolean clearCollection(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public boolean removeElement(CacheKey key, E element) {
+ return false;
+ }
+
+ @Override
+ public boolean setTimeToLive(CacheKey key, long ttl) {
+ return false;
+ }
+
+ @Override
+ public long getTimeToLive(CacheKey key) {
+ return 0;
+ }
+
+ @Override
+ public long size() {
+ return 0;
+ }
+
+ @Override
+ public boolean clear() {
+ return false;
+ }
+
+ @Override
+ public boolean exists(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public boolean remove(CacheKey key) {
+ return false;
+ }
+
+ @Override
+ public Set keySet() {
+ return null;
+ }
+ }
+}
diff --git a/ContentGrabbingJi-common/src/test/resources/META-INF/services/net.lamgc.cgj.bot.cache.CacheStoreFactory b/ContentGrabbingJi-common/src/test/resources/META-INF/services/net.lamgc.cgj.bot.cache.CacheStoreFactory
new file mode 100644
index 0000000..554544a
--- /dev/null
+++ b/ContentGrabbingJi-common/src/test/resources/META-INF/services/net.lamgc.cgj.bot.cache.CacheStoreFactory
@@ -0,0 +1,29 @@
+
+# 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 .
+#
+
+net.lamgc.cgj.bot.cache.factory.DuplicateNameFactoryA
+net.lamgc.cgj.bot.cache.factory.DuplicateNameFactoryB
+net.lamgc.cgj.bot.cache.factory.NoAnnotationFactory
+net.lamgc.cgj.bot.cache.factory.InitialFailureFactory
+net.lamgc.cgj.bot.cache.factory.AvailabilityCheckExceptionThrowFactory
+net.lamgc.cgj.bot.cache.factory.GetCacheStoreExceptionFactory
+
+net.lamgc.cgj.bot.cache.factory.LocalFactory
+net.lamgc.cgj.bot.cache.factory.MemoryFactory
+net.lamgc.cgj.bot.cache.factory.RemoteCacheFactory
+net.lamgc.cgj.bot.cache.factory.SetCacheStoreFactory
+