/**
*
* 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 java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
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.cache.thread.CacheCheckThread;
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 ApplicationCacheFactory implements CacheFactory
{
private static final Logger logger = LoggerFactory.getLogger(ApplicationCacheFactory.class);
private static volatile ApplicationCacheFactory instace = null;
private CacheController controller = null;
private Queue<CacheKey> keysList = null;
private ConcurrentMap<CacheKey, Object> entityMap = null;
private ConcurrentMap<CacheKey, CacheList> listMap = new ConcurrentHashMap<CacheKey, CacheList>(64);
private static final ReadWriteLock rwlock = new ReentrantReadWriteLock();
private static final Lock rlock = rwlock.readLock();
private static final Lock wlock = rwlock.writeLock();
private CacheConfigData appConfigData;
private CopyStrategy copyStrategy;
private ApplicationCacheFactory()
{
Configuration cfg = SessionContext.getConfigure();
CacheConfiguration cacheCfg = cfg.getCacheConfig();
if (cacheCfg == null)
return;
appConfigData = cacheCfg.getAppCacheData();
if (appConfigData == null)
return;
int initnum = 0;
int max = appConfigData.getMaxObject();
if (max < 1000)
{
initnum = max * 2 / 3;
}
else if (max < 10000)
{
initnum = max * 1 / 5;
}
else if (max > 10000)
{
initnum = max * 1 / 100;
}
keysList = new ConcurrentLinkedQueue<CacheKey>();
entityMap = new ConcurrentHashMap<CacheKey, Object>(initnum);
controller = CacheControllerFactroy.getCacheController(appConfigData, keysList, entityMap, listMap);
if (!appConfigData.isEternal())
{
Runnable checkThread = new CacheCheckThread(keysList, entityMap, listMap, appConfigData);
ThreadHelper.addThread(checkThread);
}
copyStrategy = appConfigData.getCopyStrategy();
}
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.getAppCacheData();
if (data == null)
return null;
if (instace == null)
{
instace = new ApplicationCacheFactory();
}
return instace;
}
/**
* �������
*
* @param key ����KEY
* @param obj ����ʵ��
* @throws Exception
*/
public void cacheObject(CacheKey key, Object obj)
throws CacheException
{
if (key == null || obj == null)
return;
EntityData data = key.getData();
// �������Ƿ�����л�
if (data == null || !ByteHelper.isSerializable(data))
return;
wlock.lock();
try
{
if (obj instanceof List && !appConfigData.isEntirety())
{
// ��Ϊ���������ʱ,�����б����
List<?> list = (List<?>)obj;
if (list.size() > appConfigData.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);
// ���õ��Ȳ��Դ��뻺��
controller.cacheObject(newkey, value);
map.put(newkey, value);
clist.add(newkey);
}
CacheList cacheList = new CacheList(map, clist, key.getCount());
listMap.put(key, cacheList);
}
else
{
// �������
Object value = new CacheObject(copyStrategy, key, obj);
// ���õ��Ȳ��Դ��뻺��
controller.cacheObject(key, value);
}
}
catch (Exception e)
{
throw new CacheException(e.getMessage(), e);
}
finally
{
wlock.unlock();
}
}
/**
* ��ö���
*
* @param key ����KEY
* @return ����ʵ��
* @throws Exception
*/
public Object getObject(CacheKey key)
throws Exception
{
if (key.getData() == null)
return null;
if (!key.getData().isSerializable())
return null;
rlock.lock();
try
{
// ����Ƿ��б�����ȡ��
if ((key.getCacheKeyType() == CacheKeyType.ListKey || key.getCacheKeyType() == CacheKeyType.PageKey)
&& !appConfigData.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();
}
finally
{
rlock.unlock();
}
}
/**
* �������Ƿ����ӵ�д˻���KEY�Ķ���
*
* @param key ����KEY
* @return true���� false������
*/
public boolean containKey(CacheKey key)
{
rlock.lock();
try
{
if ((key.getCacheKeyType() == CacheKeyType.ListKey || key.getCacheKeyType() == CacheKeyType.PageKey)
&& !appConfigData.isEntirety())
{
return listMap.containsKey(key);
}
return entityMap.containsKey(key);
}
finally
{
rlock.unlock();
}
}
public void flush()
{
wlock.lock();
try
{
listMap.clear();
keysList.clear();
entityMap.clear();
}
finally
{
wlock.unlock();
}
}
@Override
public void debug()
{
System.out.println(keysList.size() + " " + entityMap.size() + " " + listMap.size());
for (CacheKey key : keysList)
{
System.out.println(key.getId() + " " + key.getCacheKeyType());
}
}
}