/**
* <pre>
* This program is free software; you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* </pre>
*/
package com.meidusa.amoeba.net.poolable;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* <pre>
* ��Pool �ṩ���ؾ��⡢failover��HA����
* ����Load Balance ObjectPool����object ����ʵ��{@link PoolableObject}
* Ĭ���ṩ2�ָ��ؾ��ⷽ����
* <li>��ѯ��������ѯ���䵽ÿ��pool��ÿ��pool������Ƚ�ƽ��</li>
* <li>��æ�̶ȣ�������Pool��Active Num��һ��������С��Active Num�����ȷ�������</li>
* @author <a href=mailto:piratebase@sina.com>Struct chen</a>
* </pre>
*/
public class MultipleLoadBalanceObjectPool implements ObjectPool {
public static final int LOADBALANCING_ROUNDROBIN = 1;
public static final int LOADBALANCING_WEIGHTBASED = 2;
public static final int LOADBALANCING_HA = 3;
private boolean enable;
protected static class ActiveNumComparator implements Comparator<ObjectPool> {
public int compare(ObjectPool o1, ObjectPool o2) {
return o1.getNumActive() - o2.getNumActive();
}
}
protected static class HeartbeatDelayed implements Delayed {
private long time;
/** Sequence number to break ties FIFO */
private final long sequenceNumber;
private long NANO_ORIGIN = System.nanoTime();
private static final AtomicLong sequencer = new AtomicLong(0);
private ObjectPool pool;
private MultipleLoadBalanceObjectPool virtualPool;
public HeartbeatDelayed(long nsTime, TimeUnit timeUnit, ObjectPool pool,
MultipleLoadBalanceObjectPool virtualPool){
this.time = TimeUnit.NANOSECONDS.convert(nsTime, timeUnit);
this.pool = pool;
this.virtualPool = virtualPool;
this.sequenceNumber = sequencer.getAndIncrement();
}
public void setDelayedTime(long time, TimeUnit timeUnit) {
NANO_ORIGIN = System.nanoTime();
this.time = TimeUnit.NANOSECONDS.convert(time, timeUnit);
}
public long getDelay(TimeUnit unit) {
long d = unit.convert(time - now(), TimeUnit.NANOSECONDS);
return d;
}
public void doCheck() {
virtualPool.statusCheck(pool);
}
public int compareTo(Delayed other) {
if (other == this) // compare zero ONLY if same object
return 0;
HeartbeatDelayed x = (HeartbeatDelayed) other;
long diff = time - x.time;
if (diff < 0) return -1;
else if (diff > 0) return 1;
else if (sequenceNumber < x.sequenceNumber) return -1;
else return 1;
}
/**
* Returns nanosecond time offset by origin
*/
final long now() {
return System.nanoTime() - NANO_ORIGIN;
}
}
protected static final BlockingQueue<HeartbeatDelayed> HEART_BEAT_QUEUE = new DelayQueue<HeartbeatDelayed>();
protected static class ObjectPoolStatus {
static enum STATUS {
INVALID, VALID
};
boolean inChecked = false; ;
STATUS status;
final Lock lock = new ReentrantLock();
long lastCheckTime = System.currentTimeMillis();
ObjectPoolStatus(STATUS status){
this.status = status;
}
}
static {
new Thread() {
{
this.setDaemon(true);
this.setName("PoolHeartbeatThread");
}
public void run() {
HeartbeatDelayed delayed = null;
try {
while (true) {
delayed = HEART_BEAT_QUEUE.take();
ObjectPoolStatus status = delayed.virtualPool.statusCheck(delayed.pool);
if (status.status == ObjectPoolStatus.STATUS.INVALID) {
delayed.setDelayedTime(5, TimeUnit.SECONDS);
HEART_BEAT_QUEUE.offer(delayed);
} else {
status.lock.lock();
try {
status.inChecked = false;
} finally {
status.lock.unlock();
}
}
}
} catch (InterruptedException e) {
}
}
}.start();
}
/**
* ��������㷨
*/
private int loadbalance;
private AtomicLong currentCount = new AtomicLong(0);
private ObjectPool[] objectPools;
private ObjectPool[] runtimeObjectPools;
private Map<ObjectPool, ObjectPoolStatus> poolStatusMap = new HashMap<ObjectPool, ObjectPoolStatus>();
private ActiveNumComparator comparator = new ActiveNumComparator();
public MultipleLoadBalanceObjectPool(){
}
public MultipleLoadBalanceObjectPool(int loadbalance, ObjectPool... objectPools){
this.objectPools = objectPools;
this.runtimeObjectPools = objectPools.clone();
this.loadbalance = loadbalance;
for (ObjectPool pool : objectPools) {
poolStatusMap.put(pool, new ObjectPoolStatus(ObjectPoolStatus.STATUS.VALID));
}
}
public void setLoadbalance(int loadbalance) {
this.loadbalance = loadbalance;
}
public void setObjectPools(ObjectPool[] objectPools) {
this.objectPools = objectPools;
this.runtimeObjectPools = objectPools.clone();
poolStatusMap.clear();
for (ObjectPool pool : objectPools) {
poolStatusMap.put(pool, new ObjectPoolStatus(ObjectPoolStatus.STATUS.VALID));
}
}
public void addObject() throws Exception {
throw new UnsupportedOperationException();
}
public Object borrowObject() throws Exception {
ObjectPool pool = null;
ObjectPool[] poolsTemp = runtimeObjectPools;
if (poolsTemp.length == 0) {
throw new Exception("no valid pools");
}
if (loadbalance == LOADBALANCING_ROUNDROBIN) {
long current = currentCount.getAndIncrement();
pool = poolsTemp[(int) (current % poolsTemp.length)];
} else if (loadbalance == LOADBALANCING_WEIGHTBASED) {
if (poolsTemp.length > 1) {
ObjectPool[] objectPoolsCloned = poolsTemp.clone();
Arrays.sort(objectPoolsCloned, comparator);
pool = objectPoolsCloned[0];
} else if (poolsTemp.length == 1) {
pool = poolsTemp[0];
}
} else if (loadbalance == LOADBALANCING_HA) {
// HA,ֻҪ��Ч��pool
pool = poolsTemp[0];
} else {
throw new Exception("loadbalance parameter error,parameter loadbalance in [1,2,3]");
}
try {
return pool.borrowObject();
} catch (Exception e) {
ObjectPoolStatus status = poolStatusMap.get(pool);
status.lock.lock();
try {
if (!status.inChecked) {
status.inChecked = true;
if (status.status == ObjectPoolStatus.STATUS.VALID) {
HEART_BEAT_QUEUE.offer(new HeartbeatDelayed(1, TimeUnit.MILLISECONDS, pool, this) {
});
}
}
} finally {
status.lock.unlock();
}
throw e;
}
}
public void initAllPools() {
for (ObjectPool pool : this.objectPools) {
ObjectPoolStatus status = statusCheck(pool);
if (status.status == ObjectPoolStatus.STATUS.INVALID) {
HEART_BEAT_QUEUE.offer(new HeartbeatDelayed(2, TimeUnit.SECONDS, pool, this) {
});
}
}
}
/**
* ���ObjectPool �Ƿ��ܹ������ṩ Object�����ҽ���Ը�Pool �� runtimeObjectPools���г�Ա����
*
* @param pool
* @return ObjectPoolStatus
*/
protected synchronized ObjectPoolStatus statusCheck(ObjectPool pool) {
ObjectPoolStatus status = this.poolStatusMap.get(pool);
try {
Object object = pool.borrowObject();
pool.returnObject(object);
// ��ǰ��ö������������ǰһ״̬�Dz����õģ�����Ҫ�ı�runtimeObjectPools��Ա��
if (status.status == ObjectPoolStatus.STATUS.INVALID) {
status.status = ObjectPoolStatus.STATUS.VALID;
ObjectPool[] pools = new ObjectPool[runtimeObjectPools.length + 1];
int index = 0;
for (Map.Entry<ObjectPool, ObjectPoolStatus> entry : poolStatusMap.entrySet()) {
if (entry.getValue().status == ObjectPoolStatus.STATUS.VALID) {
pools[index++] = entry.getKey();
}
}
runtimeObjectPools = pools;
}
} catch (Exception e) {
// �������ö����ҵ�ǰ״̬�ǿ��õģ�����Ҫ����pool�� runtimeObjectPools���Ƴ���
if (status.status == ObjectPoolStatus.STATUS.VALID) {
status.status = ObjectPoolStatus.STATUS.INVALID;
ObjectPool[] pools = new ObjectPool[runtimeObjectPools.length - 1];
int index = 0;
for (Map.Entry<ObjectPool, ObjectPoolStatus> entry : poolStatusMap.entrySet()) {
if (entry.getValue().status == ObjectPoolStatus.STATUS.VALID) {
pools[index++] = entry.getKey();
}
}
runtimeObjectPools = pools;
}
}
status.lastCheckTime = System.currentTimeMillis();
return status;
}
public void clear() throws Exception, UnsupportedOperationException {
for (ObjectPool pool : objectPools) {
pool.clear();
}
}
public void close() throws Exception {
for (ObjectPool pool : objectPools) {
pool.close();
}
}
public int getNumActive() throws UnsupportedOperationException {
int active = 0;
for (ObjectPool pool : objectPools) {
active += pool.getNumActive();
}
return active;
}
public int getNumIdle() throws UnsupportedOperationException {
int idle = 0;
for (ObjectPool pool : objectPools) {
idle += pool.getNumIdle();
}
return idle;
}
public void invalidateObject(Object obj) throws Exception {
PoolableObject poolableObject = (PoolableObject) obj;
ObjectPool pool = poolableObject.getObjectPool();
pool.invalidateObject(obj);
}
public void returnObject(Object obj) throws Exception {
PoolableObject poolableObject = (PoolableObject) obj;
ObjectPool pool = poolableObject.getObjectPool();
pool.returnObject(obj);
}
public void setFactory(PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
throw new UnsupportedOperationException();
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean isEnabled) {
this.enable = isEnabled;
}
}