/* ==================================================================
* Created [2009-4-27 下午11:32:55] by Jon.King
* ==================================================================
* TSS
* ==================================================================
* mailTo:jinpujun@hotmail.com
* Copyright (c) Jon.King, 2009-2012
* ==================================================================
*/
package com.jinhe.tss.core.cachepool;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import com.jinhe.tss.core.cachepool.container.IPoolContainer;
import com.jinhe.tss.core.cachepool.strategy.CacheStrategy;
import com.jinhe.tss.core.exception.BusinessException;
/**
* <p> AbstractPool.java </p>
*
* 对象池抽象类,定义通用的方法。
*
* @author Jon.King 2007-1-3
*
*/
public abstract class AbstractPool implements IPool {
protected Logger log = Logger.getLogger(this.getClass());
// 对象池属性
protected long requests; // 请求数
protected long hits; // 命中数
protected boolean released = false; // 是否已释放
protected boolean asyncDestroy = false; // 是否异步销毁
protected ICacheLoader loader; // 缓存项载入类
protected IArithmetic arithmetic; // 缓存算法类
protected CacheStrategy strategy; // 缓存策略
protected Set<IPoolListener> listeners = new HashSet<IPoolListener> (); // 监听器列表
public abstract void init();
public abstract int getSize();
public abstract void release(boolean forced) ;
public abstract IPoolContainer getFree();
public abstract IPoolContainer getUsing();
public Cacheable getObject(Object key) {
if(key == null) {
return null;
}
Cacheable item = getOldObject(key);
addRequests();
if(item != null){
addHits();
item.addHit();
item.updateAccessed();
}
else {
//调用ICacheLoader来载入需要的缓存项,取到则放入缓存池中
item = reload(new TimeWrapper(key, null));
if(item != null) {
putObject(item.getKey(), item.getValue());
}
}
return item;
}
public Cacheable getOldObject(Object key) {
if (released) {
log.error("缓存池(" + this.getName() + ")已经被释放!");
}
return getFree().get(key) == null ? getUsing().get(key) : getFree().get(key);
}
public Cacheable putObject(Object key, Object value) {
if(key == null) {
return null;
}
Cacheable oldItem = getOldObject(key);
if(oldItem != null){
oldItem.update(value);
return oldItem;
}else{
//缓存项放入缓存池的同时也设置了其生命周期
Cacheable newItem = getFree().put(key, new TimeWrapper(key, value, strategy.getCyclelife()));
firePoolEvent(ObjectPoolEvent.PUT_IN);//事件监听器将唤醒所有等待中的线程,包括cleaner线程,checkout,remove等操作的等待线程
return newItem;
}
}
public Cacheable removeObject(Object key) {
Cacheable item = getFree().remove(key);
firePoolEvent(ObjectPoolEvent.REMOVE);
return item;
}
public List<Cacheable> listItems() {
Set<Cacheable> values = new HashSet<Cacheable>();
if(getFree() != null) {
values.addAll(getFree().getValues());
}
if(getUsing() != null) {
values.addAll(getUsing().getValues());
}
return new ArrayList<Cacheable>(values);
}
public List<CacheableKey> listKeys() {
Set<CacheableKey> keys = new HashSet<CacheableKey>();
if(getFree() != null) {
keys.addAll(getFree().getKeys());
}
if(getUsing() != null) {
keys.addAll(getUsing().getKeys());
}
return new ArrayList<CacheableKey>(keys);
}
public void flush() {
release(true);
resetHitCounter();
log.debug("已经清除池中所有的缓存项。");
}
public Cacheable reload(final Cacheable obj) throws RuntimeException {
Cacheable newObj = loader.reloadCacheObject(obj);
//如果重新加载的缓存项为空,则将原先的缓存项从缓存池中移除,否则则覆盖原有的缓存项。
if(newObj == null)
removeObject(obj.getKey());
else
newObj = putObject(newObj.getKey(), newObj.getValue());
return newObj;
}
/**
* 销毁指定对象(如果有必要的话可采用异步);
*/
public void destroyObject(final Cacheable o) {
if (o == null)
return;
if (isAsyncDestroy()){
Thread t = new Thread(new Runnable(){
public void run() {
arithmetic.destroy(o);
}
});
t.start();
}else
arithmetic.destroy(o);
}
public final void releaseAsync(final boolean forced) {
Thread t = new Thread(new Runnable(){
public void run() {
release(forced);
}
});
t.start();
}
public CacheStrategy getCacheStrategy() { return this.strategy; }
public void setCacheStrategy(CacheStrategy strategy) {
//判断是否是修改缓存策略还是在初始化缓存池,初始化的话strategy为null
if(this.getCacheStrategy() != null)
this.strategy.fireEventIfChanged(strategy);//缓存策略改变则触发事件
else
this.strategy = strategy;
}
public void setArithmetic(IArithmetic arithmetic) { this.arithmetic = arithmetic; }
public void setLoader(ICacheLoader loader) { this.loader = loader; }
public IArithmetic getArithmetic(){ return this.arithmetic; }
public String getName() { return getCacheStrategy().getName(); }
public long getRequests() {
return requests;
}
protected void addRequests() { requests++; }
protected void addHits() { hits++; }
/**
* 重置请求数和点击数
* requests = hits = 0
*/
protected final void resetHitCounter() { requests = hits = 0; }
public final float getHitRate() {
return (requests == 0) ? 0 : (((float) hits / requests) * 100f);
}
public final void setAsyncDestroy(boolean b) { asyncDestroy = b; }
public final boolean isAsyncDestroy() { return asyncDestroy; }
public final boolean isReleased() { return this.released; }
public final void firePoolEvent(int eventType) {
if (listeners.isEmpty()) return;
ObjectPoolEvent poolEvent = new ObjectPoolEvent(this, eventType);
for (Iterator<IPoolListener> iter = listeners.iterator(); iter.hasNext();){
(iter.next()).dealwithPoolEvent(poolEvent);
}
}
public final void addObjectPoolListener(IPoolListener x){
listeners.add(x);
}
public final void removeObjectPoolListener(IPoolListener x){
listeners.remove(x);
}
protected Cacheable checkOut() {
Cacheable item = getFree().getByAccessMethod(getCacheStrategy().getAccessMethod());
if(item != null){
item.addHit();
getFree().remove(item.getKey());
getUsing().put(item.getKey(), item);
}
return item;
}
public Cacheable checkOut(long timeout) {
if(timeout <= 0) {
timeout = this.getCacheStrategy().getInterruptTime();
}
long time = System.currentTimeMillis();
Cacheable o = null;
o = checkOut();
synchronized(this){
while (o == null && (System.currentTimeMillis() - time < timeout)) {
try {
log.debug("缓存池(" + this.getName() + ")中没有可用的缓存项......等待 " + timeout + "(毫秒)");
wait(timeout);
o = checkOut();
}catch (InterruptedException e) {
log.error("检出时等待被中断", e);
}
}
}
if(o == null){
String errorMsg = "缓存池(" + this.getName() + ")已满,且各缓存项都处于使用状态,需要等待。可考虑重新设置缓存策略!";
log.error(errorMsg);
throw new BusinessException(errorMsg);
}
return o;
}
public void checkIn(Cacheable o) {
if (o == null){
log.error("试图返回空的缓存项");
return;
}
//判断对象是否存在using池中,是的话将对象从using池中移出,否则抛出异常
if(!o.equals(getUsing().remove(o.getKey()))){
log.error("试图返回不是using池中对象到free中,返回失败! " + getName());
throw new BusinessException("试图返回不是using池中对象到free中,返回失败! " + getName());
}
Object value = o.getValue();
//如果池已满,则销毁对象,否则则放回池中
int maxSize = getCacheStrategy().getPoolSize().intValue();
if (maxSize > 0 && getSize() >= maxSize){
destroyObject(o);
}else{
try{
// 如果对象实现了Reusable接口,则执行重置操作
if(value instanceof Reusable) {
((Reusable)value).recycle();
}
getFree().put(o.getKey(), o); //重新放入free池中,其点击率等属性已改变
firePoolEvent(ObjectPoolEvent.CHECKIN);//事件监听器将唤醒所有等待中的线程,包括cleaner线程,checkout,remove等操作的等待线程
log.debug(o.getValue() + " 缓存项已经回收!");
}catch (Exception e){
destroyObject(o); // 如果不能回收则销毁
log.error("无法回收缓存项,已销毁!", e);
}
}
}
public Cacheable remove() {
Cacheable o = getFree().getByAccessMethod(getCacheStrategy().getAccessMethod().intValue());
//如果free池中取不到,则要等using池中的缓存对象返回到free中。线程等待
long timeout = getCacheStrategy().getInterruptTime().longValue();
long time = System.currentTimeMillis();
synchronized(this){
while (o == null && (System.currentTimeMillis() - time < timeout)) {
try {
log.debug("free缓存池(" + this.getName() + ")中没有可用的项......等待 " + timeout + "ms");
wait(timeout);
o = checkOut();
}catch (InterruptedException e) {
log.error("等待移除 checkIn 对象时等待被中断", e);
}
}
}
removeObject(o.getKey());
return o;
}
public boolean purge() {
log.debug("开始清除 (\"" + this.getName() + "\") 池中过期的缓存项 ....... ");
int count = 0;
Cacheable item = null;
for (CacheableKey key : getFree().getKeys()){
item = getFree().get(key);
if (item != null && item.isExpired()){
removeObject(item.getKey());
destroyObject(item);
count++;
}
}
log.debug("共清除了 (\"" + this.getName() + "\") 池中 " + count + " 个缓存对象。");
return getFree().size() > 0 || count > 0;
}
}