/**
*
* Copyright 2014 The Darks ORM Project (Liu lihua)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package darks.orm.core.cache.scope;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import net.sf.ehcache.CacheException;
import darks.orm.core.cache.CacheContext.CacheKeyType;
import darks.orm.core.cache.CacheController;
import darks.orm.core.cache.CacheKey;
import darks.orm.core.cache.CacheList;
import darks.orm.core.cache.CacheObject;
import darks.orm.core.cache.control.CacheControllerFactroy;
import darks.orm.core.cache.strategy.CopyStrategy;
import darks.orm.core.config.CacheConfiguration;
import darks.orm.core.config.Configuration;
import darks.orm.core.data.EntityData;
import darks.orm.core.data.FieldData;
import darks.orm.core.data.xml.CacheConfigData;
import darks.orm.core.session.SessionContext;
import darks.orm.log.Logger;
import darks.orm.log.LoggerFactory;
import darks.orm.util.ByteHelper;
import darks.orm.util.ThreadHelper;
public class ThreadCacheFactory implements CacheFactory
{
private static final Logger logger = LoggerFactory.getLogger(ThreadCacheFactory.class);
private static volatile ThreadCacheFactory instace = null;
private static final ThreadLocal<Queue<CacheKey>> threadKeysList = new ThreadLocal<Queue<CacheKey>>();
private static final ThreadLocal<Map<CacheKey, Object>> threadEntityMap = new ThreadLocal<Map<CacheKey, Object>>();
private static final ThreadLocal<Map<CacheKey, CacheList>> threadListMap =
new ThreadLocal<Map<CacheKey, CacheList>>();
private static final ThreadLocal<CacheController> threadController = new ThreadLocal<CacheController>();
private static final ConcurrentMap<Long, Thread> threadMap = new ConcurrentHashMap<Long, Thread>(256);
private CacheConfigData threadConfigData;
private CopyStrategy copyStrategy;
private ThreadCacheFactory()
{
try
{
Configuration cfg = SessionContext.getConfigure();
CacheConfiguration cacheCfg = cfg.getCacheConfig();
if (cacheCfg == null)
return;
threadConfigData = cacheCfg.getThreadCacheData();
if (threadConfigData == null)
return;
copyStrategy = threadConfigData.getCopyStrategy();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static CacheFactory getInstance()
{
Configuration cfg = SessionContext.getConfigure();
if (cfg == null)
return null;
CacheConfiguration cacheCfg = cfg.getCacheConfig();
if (cacheCfg == null)
return null;
CacheConfigData data = cacheCfg.getThreadCacheData();
if (data == null)
return null;
if (instace == null)
{
instace = new ThreadCacheFactory();
}
return instace;
}
/**
* �������
*
* @param key ����KEY
* @param obj ����ʵ��
* @throws Exception
*/
public void cacheObject(CacheKey key, Object obj)
throws Exception
{
if (key == null || obj == null)
return;
EntityData data = key.getData();
if (data == null || !ByteHelper.isSerializable(data))
return;
Map<CacheKey, CacheList> listMap = getListMap();
CacheController controller = getCacheController();
Thread t = Thread.currentThread();
if (!threadMap.containsKey(t.getId()))
{
threadMap.put(t.getId(), t);
}
try
{
if (obj instanceof List && !threadConfigData.isEntirety())
{
List list = (List)obj;
if (list.size() > threadConfigData.getMaxObject())
{
throw new CacheException("the size of list cacheing is flowover the max limit");
}
FieldData pkfdata = data.getPkField();
Map<CacheKey, Object> map = new ConcurrentHashMap<CacheKey, Object>();
List<CacheKey> clist = new ArrayList<CacheKey>();
for (Object ob : list)
{
int piId = (Integer)pkfdata.getValue(ob);
CacheKey newkey = new CacheKey(data, piId, CacheKeyType.SingleKey);
Object value = new CacheObject(copyStrategy, newkey, ob);// new
// SoftReference<Object>(ob);//(Object)ByteHelper.ObjectToByte(ob);
controller.cacheObject(newkey, value);
map.put(newkey, value);
clist.add(newkey);
}
CacheList cacheList = new CacheList(map, clist);
listMap.put(key, cacheList);
}
else
{
Object value = new CacheObject(copyStrategy, key, obj);// new
// SoftReference<Object>(obj);//(Object)ByteHelper.ObjectToByte(obj);
controller.cacheObject(key, value);
}
}
catch (Exception e)
{
throw new CacheException(e.getMessage(), e);
}
}
/**
* ��ö���
*
* @param key ����KEY
* @return ����ʵ��
* @throws Exception
*/
public Object getObject(CacheKey key)
throws Exception
{
if (key.getData() == null)
return false;
if (!key.getData().isSerializable())
return false;
Map<CacheKey, CacheList> listMap = getListMap();
CacheController controller = getCacheController();
try
{
if ((key.getCacheKeyType() == CacheKeyType.ListKey || key.getCacheKeyType() == CacheKeyType.PageKey)
&& !threadConfigData.isEntirety())
{
CacheList cacheList = listMap.get(key);
if (cacheList == null)
return null;
key.setCount(cacheList.getCount());
List<CacheKey> clist = cacheList.getList();
List<Object> list = new ArrayList<Object>(clist.size());
for (CacheKey ckey : clist)
{
CacheObject val = (CacheObject)controller.getObject(ckey);
if (val == null)
return null;
val.setLastIdleTime(System.currentTimeMillis());
list.add(val.getObject());
}
return list;
}
CacheObject value = (CacheObject)controller.getObject(key);
if (value == null)
return null;
key.setCount(value.getKey().getCount());
value.setLastIdleTime(System.currentTimeMillis());
return value.getObject();
}
catch (Exception e)
{
throw e;
}
}
/**
* �������Ƿ����ӵ�д˻���KEY�Ķ���
*
* @param key ����KEY
* @return true���� false������
*/
public boolean containKey(CacheKey key)
{
Map<CacheKey, Object> entityMap = getEntityMap();
Map<CacheKey, CacheList> listMap = getListMap();
if ((key.getCacheKeyType() == CacheKeyType.ListKey || key.getCacheKeyType() == CacheKeyType.PageKey)
&& !threadConfigData.isEntirety())
{
return listMap.containsKey(key);
}
return entityMap.containsKey(key);
}
/**
* ��ü�ֵ�б�
*
* @return ��ֵ����
*/
private Queue<CacheKey> getKeysList()
{
Queue<CacheKey> keysList = threadKeysList.get();
if (keysList == null)
{
keysList = new ConcurrentLinkedQueue<CacheKey>();
threadKeysList.set(keysList);
}
return keysList;
}
/**
* ���ʵ��MAP
*
* @return MAP
*/
private Map<CacheKey, Object> getEntityMap()
{
Map<CacheKey, Object> entityMap = threadEntityMap.get();
if (entityMap == null)
{
int initnum = 0;
int max = threadConfigData.getMaxObject();
if (max < 1000)
{
initnum = max * 2 / 3;
}
else if (max < 10000)
{
initnum = max * 1 / 5;
}
else if (max > 10000)
{
initnum = max * 1 / 100;
}
entityMap = new ConcurrentHashMap<CacheKey, Object>(initnum);
threadEntityMap.set(entityMap);
}
return entityMap;
}
private Map<CacheKey, CacheList> getListMap()
{
Map<CacheKey, CacheList> listMap = threadListMap.get();
if (listMap == null)
{
listMap = new ConcurrentHashMap<CacheKey, CacheList>(64);
threadListMap.set(listMap);
}
return listMap;
}
private CacheController getCacheController()
{
CacheController ctrl = threadController.get();
if (ctrl == null)
{
Queue<CacheKey> keysList = getKeysList();
Map<CacheKey, Object> entityMap = getEntityMap();
Map<CacheKey, CacheList> listMap = getListMap();
ctrl = CacheControllerFactroy.getCacheController(threadConfigData, keysList, entityMap, listMap);
threadController.set(ctrl);
}
return ctrl;
}
public void debug()
{
Queue<CacheKey> keysList = getKeysList();
Map<CacheKey, Object> entityMap = getEntityMap();
Map<CacheKey, CacheList> listMap = getListMap();
System.out.println(keysList.size() + " " + entityMap.size() + " " + listMap.size());
for (CacheKey key : keysList)
{
System.out.println(key.getId() + " " + key.getCacheKeyType());
}
}
public void flush()
{
Queue<CacheKey> keysList = getKeysList();
Map<CacheKey, Object> entityMap = getEntityMap();
Map<CacheKey, CacheList> listMap = getListMap();
try
{
listMap.clear();
keysList.clear();
entityMap.clear();
cleanThreadLocals();
}
catch (Exception e)
{
}
}
private void cleanThreadLocals()
throws NoSuchFieldException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException
{
try
{
for (Thread t : threadMap.values())
{
ThreadHelper.cleanThreadLocals(t);
}
threadMap.clear();
}
finally
{
}
}
}