
cn.leancloud.cache.QueryResultCache Maven / Gradle / Ivy
package cn.leancloud.cache;
import cn.leancloud.AVLogger;
import cn.leancloud.AVObject;
import cn.leancloud.codec.MD5;
import cn.leancloud.core.AppConfiguration;
import cn.leancloud.query.AVQueryResult;
import cn.leancloud.utils.LogUtil;
import cn.leancloud.utils.StringUtil;
import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public class QueryResultCache extends LocalStorage {
private static final AVLogger LOGGER = LogUtil.getLogger(QueryResultCache.class);
private static QueryResultCache INSTANCE = null;
private ExecutorService executor = Executors.newFixedThreadPool(2);
public static synchronized QueryResultCache getInstance() {
if (null == INSTANCE) {
INSTANCE = new QueryResultCache();
}
return INSTANCE;
}
private QueryResultCache() {
super(AppConfiguration.getQueryResultCacheDir());
}
public String cacheResult(String key, String content) {
LOGGER.d("save cache. key=" + key + ", value=" + content);
try {
return super.saveData(key, content.getBytes("UTF-8"));
} catch (Exception ex) {
LOGGER.w(ex);
return null;
}
}
public static String generateKeyForQueryCondition(String className, Map query) {
StringBuilder sb = new StringBuilder();
sb.append(className);
sb.append(":");
for (Map.Entry entry: query.entrySet()) {
sb.append(entry.getKey());
sb.append("=");
sb.append(entry.getValue());
sb.append("&");
}
return MD5.computeMD5(sb.toString());
}
public static String generateCachedKey(String className, Map params) {
StringBuilder sb = new StringBuilder();
sb.append(className);
sb.append(":");
for (Map.Entry entry: params.entrySet()) {
sb.append(entry.getKey());
sb.append("=");
sb.append(entry.getValue().toString());
sb.append("&");
}
return MD5.computeMD5(sb.toString());
}
public boolean hasCachedResult(String className, Map query, long maxAgeInMilliseconds) {
String cacheKey = generateKeyForQueryCondition(className, query);
File cacheFile = getCacheFile(cacheKey);
if (null == cacheFile || !cacheFile.exists()) {
LOGGER.d("cache file(key=" + cacheKey + ") not existed.");
return false;
}
if (maxAgeInMilliseconds > 0 && (System.currentTimeMillis() - cacheFile.lastModified() > maxAgeInMilliseconds)) {
LOGGER.d("cache file(key=" + cacheKey + ") is expired.");
return false;
}
return true;
}
public Observable getCacheRawResult(final String className, final Map query,
final long maxAgeInMilliseconds, final boolean isFinal) {
LOGGER.d("try to get cache raw result for class:" + className);
String cacheKey = generateKeyForQueryCondition(className, query);
return getCacheRawResult(className, cacheKey, maxAgeInMilliseconds, isFinal);
}
public Observable getCacheRawResult(final String className, final String cacheKey,
final long maxAgeInMilliseconds, final boolean isFinal) {
LOGGER.d("try to get cache raw result for class:" + className);
AppConfiguration.SchedulerCreator creator = AppConfiguration.getDefaultScheduler();
boolean isAsync = AppConfiguration.isAsynchronized();
Callable callable = new Callable() {
public String call() throws Exception {
File cacheFile = getCacheFile(cacheKey);
if (null == cacheFile || !cacheFile.exists()) {
LOGGER.d("cache file(key=" + cacheKey + ") not existed.");
if (isFinal) {
throw new FileNotFoundException("cache is not existed.");
} else {
return "";
}
}
if (maxAgeInMilliseconds > 0 && (System.currentTimeMillis() - cacheFile.lastModified() > maxAgeInMilliseconds)) {
LOGGER.d("cache file(key=" + cacheKey + ") is expired.");
if (isFinal) {
throw new FileNotFoundException("cache file is expired.");
} else {
return "";
}
}
byte[] data = readData(cacheFile);
if (null == data) {
LOGGER.d("cache file(key=" + cacheKey + ") is empty.");
if (isFinal) {
throw new InterruptedException("failed to read cache file.");
} else {
return "";
}
}
String content = new String(data, 0, data.length, "UTF-8");
LOGGER.d("cache file(key=" + cacheKey + "), content: " + content);
return content;
}
};
FutureTask futureTask = new FutureTask<>(callable);
executor.submit(futureTask);
Observable result = Observable.fromFuture(futureTask);
if (isAsync) {
result = result.subscribeOn(Schedulers.io());
}
if (null != creator) {
result = result.observeOn(creator.create());
}
return result;
}
public Observable> getCacheResult(final String className, final Map query,
final long maxAgeInMilliseconds, final boolean isFinal) {
LOGGER.d("try to get cache result for class:" + className);
Callable> callable = new Callable>() {
public List call() throws Exception {
String cacheKey = generateKeyForQueryCondition(className, query);
File cacheFile = getCacheFile(cacheKey);
if (null == cacheFile || !cacheFile.exists()) {
LOGGER.d("cache file(key=" + cacheKey + ") not existed.");
if (isFinal) {
return new ArrayList<>();
} else {
throw new FileNotFoundException("cache is not existed.");
}
}
if (maxAgeInMilliseconds > 0 && (System.currentTimeMillis() - cacheFile.lastModified() > maxAgeInMilliseconds)) {
LOGGER.d("cache file(key=" + cacheKey + ") is expired.");
if (isFinal) {
return new ArrayList<>();
} else {
throw new FileNotFoundException("cache file is expired.");
}
}
byte[] data = readData(cacheFile);
if (null == data) {
LOGGER.d("cache file(key=" + cacheKey + ") is empty.");
if (isFinal) {
return new ArrayList<>();
} else {
throw new InterruptedException("failed to read cache file.");
}
}
String content = new String(data, 0, data.length, "UTF-8");
LOGGER.d("cache file(key=" + cacheKey + "), content: " + content);
AVQueryResult result = AVQueryResult.fromJSONString(content);
return result.getResults();
}
};
FutureTask> futureTask = new FutureTask>(callable);
executor.submit(futureTask);
return Observable.fromFuture(futureTask);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy