package com.jarvis.cache.map;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jarvis.cache.serializer.HessianSerializer;
import com.jarvis.cache.serializer.ISerializer;
import com.jarvis.cache.to.CacheWrapper;
import com.jarvis.lib.util.OsUtil;
public class CacheTask implements Runnable, CacheChangeListener {
private static final Logger logger=LoggerFactory.getLogger(CacheTask.class);
/**
* 缓存被修改的个数
*/
private AtomicInteger cacheChanged=new AtomicInteger(0);
private CachePointCut cacheManager;
private volatile boolean running=false;
private File saveFile;
private ISerializer<Object> persistSerializer;
public CacheTask(CachePointCut cacheManager) {
this.cacheManager=cacheManager;
}
public void start() {
if(!this.running) {
loadCache();
this.running=true;
}
}
public void destroy() {
persistCache(true);
this.running=false;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
this.running=false;
}
private String getSavePath() {
String persistFile=cacheManager.getPersistFile();
if(null != persistFile && persistFile.trim().length() > 0) {
return persistFile;
}
String path="/tmp/autoload-cache/";
String nsp=cacheManager.getNamespace();
if(null != nsp && nsp.trim().length() > 0) {
path+=nsp.trim() + "/";
}
if(OsUtil.getInstance().isLinux()) {
return path;
}
return "C:" + path;
}
private File getSaveFile() {
if(null != saveFile) {
return saveFile;
}
String path=getSavePath();
File savePath=new File(path);
if(!savePath.exists()) {
savePath.mkdirs();
}
saveFile=new File(path + "map.cache");
return saveFile;
}
private ISerializer<Object> getPersistSerializer() {// 因为只有HessianSerializer才支持SoftReference序列化,所以只能使用HessianSerializer
if(null == persistSerializer) {
if(null != cacheManager.getSerializer() && cacheManager.getSerializer() instanceof HessianSerializer) {
persistSerializer=cacheManager.getSerializer();
} else {
persistSerializer=new HessianSerializer();
}
}
return persistSerializer;
}
/**
* 从磁盘中加载之前保存的缓存数据,避免刚启动时,因为没有缓存,而且造成压力过大
*/
@SuppressWarnings("unchecked")
public void loadCache() {
if(!cacheManager.isNeedPersist()) {
return;
}
File file=getSaveFile();
if(null == file) {
return;
}
if(!file.exists()) {
return;
}
BufferedInputStream bis=null;
try {
FileInputStream fis=new FileInputStream(file);
bis=new BufferedInputStream(fis);
ByteArrayOutputStream baos=new ByteArrayOutputStream();
byte buf[]=new byte[1024];
int len=-1;
while((len=bis.read(buf)) != -1) {
baos.write(buf, 0, len);
}
byte retArr[]=baos.toByteArray();
Object obj=getPersistSerializer().deserialize(retArr, null);
if(null != obj && obj instanceof ConcurrentHashMap) {
cacheManager.getCache().putAll((ConcurrentHashMap<String, Object>)obj);
}
} catch(Exception ex) {
logger.error(ex.getMessage(), ex);
} finally {
if(null != bis) {
try {
bis.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
}
private void persistCache(boolean force) {
if(!cacheManager.isNeedPersist()) {
return;
}
int cnt=cacheChanged.intValue();
if(!force && cnt <= cacheManager.getUnpersistMaxSize()) {
return;
}
cacheChanged.set(0);
FileOutputStream fos=null;
try {
byte[] data=getPersistSerializer().serialize(cacheManager.getCache());
File file=getSaveFile();
fos=new FileOutputStream(file);
fos.write(data);
} catch(Exception ex) {
cacheChanged.addAndGet(cnt);
logger.error(ex.getMessage(), ex);
} finally {
if(null != fos) {
try {
fos.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public void run() {
while(running) {
try {
cleanCache();
persistCache(false);
} catch(Exception e) {
logger.error(e.getMessage(), e);
}
try {
Thread.sleep(cacheManager.getClearAndPersistPeriod());
} catch(InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
}
/**
* 清除过期缓存
*/
private void cleanCache() {
Iterator<Entry<String, Object>> iterator=cacheManager.getCache().entrySet().iterator();
int _cacheChanged=0;
int i=0;
while(iterator.hasNext()) {
_cacheChanged+=removeExpiredItem(iterator);
i++;
if(i == 2000) {
i=0;
try {
Thread.sleep(0);// 触发操作系统立刻重新进行一次CPU竞争, 让其它线程获得CPU控制权的权力。
} catch(InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
}
if(_cacheChanged > 0) {
cacheChange(_cacheChanged);
}
}
@SuppressWarnings("unchecked")
private int removeExpiredItem(Iterator<Entry<String, Object>> iterator) {
int _cacheChanged=0;
Object value=iterator.next().getValue();
if(value instanceof SoftReference) {
SoftReference<CacheWrapper<Object>> reference=(SoftReference<CacheWrapper<Object>>)value;
if(null != reference && null != reference.get()) {
CacheWrapper<Object> tmp=reference.get();
if(tmp.isExpired()) {
iterator.remove();
_cacheChanged++;
}
} else {
iterator.remove();
_cacheChanged++;
}
} else if(value instanceof ConcurrentHashMap) {
ConcurrentHashMap<String, Object> hash=(ConcurrentHashMap<String, Object>)value;
Iterator<Entry<String, Object>> iterator2=hash.entrySet().iterator();
while(iterator2.hasNext()) {
Object tmpObj=iterator2.next().getValue();
if(tmpObj instanceof SoftReference) {
SoftReference<CacheWrapper<Object>> reference=(SoftReference<CacheWrapper<Object>>)tmpObj;
if(null != reference && null != reference.get()) {
CacheWrapper<Object> tmp=reference.get();
if(tmp.isExpired()) {
iterator2.remove();
_cacheChanged++;
}
} else {
iterator2.remove();
_cacheChanged++;
}
} else if(tmpObj instanceof CacheWrapper) {// 兼容老版本
CacheWrapper<Object> tmp=(CacheWrapper<Object>)tmpObj;
if(tmp.isExpired()) {
iterator2.remove();
_cacheChanged++;
}
}
}
if(hash.isEmpty()) {
iterator.remove();
}
} else {
CacheWrapper<Object> tmp=(CacheWrapper<Object>)value;
if(tmp.isExpired()) {
iterator.remove();
_cacheChanged++;
}
}
return _cacheChanged;
}
@Override
public void cacheChange() {
cacheChanged.incrementAndGet();
}
@Override
public void cacheChange(int cnt) {
cacheChanged.addAndGet(cnt);
}
}