/**
* <pre>
* This program is free software; you can redistribute it and/or modify it under the terms of
* the GNU AFFERO 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 AFFERO GENERAL PUBLIC LICENSE for more details.
* You should have received a copy of the GNU AFFERO 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.context;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.osgi.framework.Bundle;
import com.meidusa.amoeba.config.BeanObjectEntityConfig;
import com.meidusa.amoeba.config.DBServerConfig;
import com.meidusa.amoeba.config.ProxyServerConfig;
import com.meidusa.amoeba.config.UserConfig;
import com.meidusa.amoeba.config.loader.AmoebaContextLoader;
import com.meidusa.amoeba.exception.AmoebaRuntimeException;
import com.meidusa.amoeba.exception.ConfigurationException;
import com.meidusa.amoeba.exception.InitialisationException;
import com.meidusa.amoeba.exception.UpdateDBServerRuntimeException;
import com.meidusa.amoeba.heartbeat.HeartbeatDelayed;
import com.meidusa.amoeba.heartbeat.Status;
import com.meidusa.amoeba.net.ConnectionManager;
import com.meidusa.amoeba.net.poolable.MultipleLoadBalanceObjectPool;
import com.meidusa.amoeba.net.poolable.ObjectPool;
import com.meidusa.amoeba.net.poolable.PoolableObject;
import com.meidusa.amoeba.route.QueryRouter;
import com.meidusa.amoeba.seq.fetcher.SeqFetchService;
import com.meidusa.amoeba.util.Initialisable;
import com.meidusa.amoeba.util.Reporter;
import com.meidusa.amoeba.util.StringUtil;
/**
* @author <a href=mailto:piratebase@sina.com>Struct chen</a>
*/
public class ProxyRuntimeContext implements Reporter {
public static final String DEFAULT_SERVER_CONNECTION_MANAGER_CLASS =
"com.meidusa.amoeba.net.AuthingableConnectionManager";
public static final String DEFAULT_REAL_POOL_CLASS =
"com.meidusa.amoeba.net.poolable.PoolableObjectPool";
public static final String DEFAULT_VIRTUAL_POOL_CLASS =
"com.meidusa.amoeba.server.MultipleServerPool";
protected static Logger logger = Logger.getLogger(ProxyRuntimeContext.class);
private static ProxyRuntimeContext context = null;
private ProxyServerConfig config;
private Map<String, ConnectionManager> conMgrMap =
new ConcurrentHashMap<String, ConnectionManager>();
private Map<String, ObjectPool> poolMap = new ConcurrentHashMap<String, ObjectPool>();
private Map<String, UserConfig> userMap = new ConcurrentHashMap<String, UserConfig>();
private List<ContextChangedListener> listeners =
new CopyOnWriteArrayList<ContextChangedListener>();
@SuppressWarnings("unchecked")
private QueryRouter queryRouter;
private RuntimeContext runtimeContext;
private Map<String, Object> beanContext = new ConcurrentHashMap<String, Object>();
//可能是mysql, mongodb, aladdin 插件中的一个,为了获得他们的类加载器
private Bundle backendBundle;
// 兼容文件加载
private String amoebaHomePath;
// 后端因超负载,验证超时个数
private AtomicInteger connTimeOutCount;
private AmoebaContextLoader amoebaLoader;
public void setAmoebaLoader(AmoebaContextLoader amoebaLoader) {
this.amoebaLoader = amoebaLoader;
}
public RuntimeContext getRuntimeContext() {
return runtimeContext;
}
public static ProxyRuntimeContext getInstance() {
return context;
}
public boolean isUserExisted(String username) {
if (userMap.containsKey(username)) {
return true;
}
return false;
}
public UserConfig getUserConfigByName(String username) {
return userMap.get(username);
}
public static void setInstance(ProxyRuntimeContext context) {
ProxyRuntimeContext.context = context;
}
public String getDefaultServerConnectionManagerClassName() {
return DEFAULT_SERVER_CONNECTION_MANAGER_CLASS;
}
public String getDefaultRealPoolClassName() {
return DEFAULT_REAL_POOL_CLASS;
}
public String getDefaultVirtualPoolClassName() {
return DEFAULT_VIRTUAL_POOL_CLASS;
}
public ProxyServerConfig getConfig() {
return config;
}
public QueryRouter getQueryRouter() {
return queryRouter;
}
public Bundle getBackendBundle() {
return backendBundle;
}
public void setBackendBundle(Bundle backendBundle) {
this.backendBundle = backendBundle;
}
public Map<String, ConnectionManager> getConnectionManagerList() {
return conMgrMap;
}
public Map<String, ObjectPool> getPoolMap() {
return poolMap;
}
public Map<String, UserConfig> getUserMap() {
return userMap;
}
public String getAmoebaHomePath() {
return amoebaHomePath;
}
public void setAmoebaHomePath(String amoebaHomePath) {
this.amoebaHomePath = amoebaHomePath;
}
public AtomicInteger getConnTimeOutCount() {
return connTimeOutCount;
}
private List<Initialisable> initialisableList = new ArrayList<Initialisable>();
/**
* db server间的配置继承
*
* @param parent
* @param dest
* @return
*/
protected void inheritDBServerConfig(DBServerConfig parent, DBServerConfig dest) {
BeanObjectEntityConfig destBeanConfig = dest.getFactoryConfig();
BeanObjectEntityConfig parentBeanConfig = parent.getFactoryConfig();
if (destBeanConfig != null) {
if (parentBeanConfig != null) {
inheritBeanObjectEntityConfig(parentBeanConfig, destBeanConfig);
}
} else {
dest.setFactoryConfig(parentBeanConfig);
}
destBeanConfig = dest.getPoolConfig();
parentBeanConfig = parent.getPoolConfig();
if (destBeanConfig != null) {
if (parentBeanConfig != null) {
inheritBeanObjectEntityConfig(parentBeanConfig, destBeanConfig);
}
} else {
dest.setPoolConfig(parentBeanConfig);
}
if (dest.getVirtual() == null) {
dest.setVirtual(parent.getVirtual());
}
if (dest.getAbstractive() == null) {
dest.setAbstractive(parent.getAbstractive());
}
}
public void addContextChangedListener(ContextChangedListener listener) {
if (!listeners.contains(listener)) {
this.listeners.add(listener);
}
}
public void removeContextChangedListener(ContextChangedListener listener) {
this.listeners.remove(listener);
}
public void notifyAllContextChangedListener() {
for (ContextChangedListener listener : listeners) {
listener.doChange();
}
}
protected void inheritBeanObjectEntityConfig(BeanObjectEntityConfig parent,
BeanObjectEntityConfig dest) {
BeanObjectEntityConfig parentCloned = (BeanObjectEntityConfig) parent.clone();
if (!StringUtil.isEmpty(dest.getClassName())) {
parentCloned.setClassName(dest.getClassName());
}
/*
* if(!StringUtil.isEmpty(dest.getName())){ parentCloned.setName(dest.getName()); }
*/
if (dest.getParams() != null) {
if (parentCloned.getParams() == null) {
parentCloned.setParams(dest.getParams());
} else {
parentCloned.getParams().putAll(dest.getParams());
}
}
dest.setClassName(parentCloned.getClassName());
// dest.setName(parentCloned.getName());
dest.setParams(parentCloned.getParams());
}
public Object createBeanObjectEntity(BeanObjectEntityConfig config, boolean initEarly) {
Object object = config.createBeanObject(initEarly);
if (!StringUtil.isEmpty(config.getName())) {
beanContext.put(config.getName(), object);
}
return object;
}
public void initConfig() {
config = amoebaLoader.loadConfig();
}
public void init() {
this.runtimeContext = (RuntimeContext) createBeanObjectEntity(config.getRuntimeConfig(), true);
/*
* for (Map.Entry<String, BeanObjectEntityConfig> entry : config.getManagers().entrySet()) {
* BeanObjectEntityConfig beanObjectEntityConfig = entry.getValue(); try { ConnectionManager
* manager = (ConnectionManager) beanObjectEntityConfig.createBeanObject(false);
* manager.setName(entry.getKey()); initialisableList.add(manager);
* conMgrMap.put(manager.getName(), manager); } catch (Exception e) { throw new
* ConfigurationException("manager instance error", e); } }
*/
for (Map.Entry<String, DBServerConfig> entry : config.getDbServers().entrySet()) {
DBServerConfig dbServerConfig = (DBServerConfig) entry.getValue().clone();
String parent = dbServerConfig.getParent();
if (!StringUtil.isEmpty(parent)) {
DBServerConfig parentConfig = config.getDbServers().get(parent);
if (parentConfig == null || parentConfig.getParent() != null) {
throw new ConfigurationException(entry.getKey() + " cannot found parent with name="
+ parent + " or parent's parent must be null");
}
inheritDBServerConfig(parentConfig, dbServerConfig);
}
// ignore if dbServer is abstract
if (dbServerConfig.getAbstractive() != null && dbServerConfig.getAbstractive().booleanValue()) {
continue;
}
try {
BeanObjectEntityConfig poolConfig = dbServerConfig.getPoolConfig();
ObjectPool pool = (ObjectPool) poolConfig.createBeanObject(false, conMgrMap);
pool.setName(StringUtil.isEmpty(dbServerConfig.getName())
? poolConfig.getName()
: dbServerConfig.getName());
if (pool instanceof Initialisable) {
initialisableList.add((Initialisable) pool);
}
// 一般来说,virtual的factory config为null,但是为了避免因为错误配置而导致初始化virtual pool,这里需要
// 排除virtual pool
if (dbServerConfig.getFactoryConfig() != null && !dbServerConfig.getVirtual()) {
PoolableObjectFactory factory =
(PoolableObjectFactory) dbServerConfig.getFactoryConfig().createBeanObject(false,
conMgrMap);
if (factory instanceof Initialisable) {
initialisableList.add((Initialisable) factory);
}
pool.setFactory(factory);
}
poolMap.put(entry.getKey(), pool);
} catch (Exception e) {
throw new ConfigurationException("createBean error dbServer=" + dbServerConfig.getName(), e);
}
}
if (config.getQueryRouterConfig() != null) {
BeanObjectEntityConfig queryRouterConfig = config.getQueryRouterConfig();
try {
queryRouter = (QueryRouter) queryRouterConfig.createBeanObject(false, conMgrMap);
if (queryRouter instanceof Initialisable) {
initialisableList.add((Initialisable) queryRouter);
}
if (queryRouter instanceof ContextChangedListener) {
this.addContextChangedListener((ContextChangedListener) queryRouter);
}
} catch (Exception e) {
throw new ConfigurationException("queryRouter instance error", e);
}
}
initAllInitialisableBeans();
initialisableList.clear();
for (ConnectionManager cm : getConnectionManagerList().values()) {
cm.start();
}
initPools();
connTimeOutCount = new AtomicInteger(0);
// 全局序列服务初始化
try {
SeqFetchService.init();
} catch (Exception e) {
logger.error("init sequence service error!");
throw new AmoebaRuntimeException(e);
}
}
protected void initPools() {
for (Map.Entry<String, ObjectPool> entry : poolMap.entrySet()) {
ObjectPool pool = entry.getValue();
if (pool instanceof MultipleLoadBalanceObjectPool) {
MultipleLoadBalanceObjectPool multiPool = (MultipleLoadBalanceObjectPool) pool;
multiPool.initAllPools();
} else {
PoolableObject object = null;
try {
object = (PoolableObject) pool.borrowObject();
} catch (Exception e) {
logger.error("init pool " + pool.getName() + " error!", e);
} finally {
if (object != null) {
try {
pool.returnObject(object);
} catch (Exception e) {
logger.error("return init pools error", e);
}
}
}
}
if (pool instanceof ContextChangedListener) {
this.addContextChangedListener((ContextChangedListener) pool);
}
}
}
private void initAllInitialisableBeans() {
for (Initialisable bean : initialisableList) {
try {
bean.init();
if (bean instanceof ContextChangedListener) {
this.addContextChangedListener((ContextChangedListener) bean);
}
} catch (InitialisationException e) {
throw new ConfigurationException("Initialisation bean=" + bean + " error", e);
}
}
}
public void appendReport(StringBuilder buffer, long now, long sinceLast, boolean reset,
Level level) {
for (Map.Entry<String, ObjectPool> entry : getPoolMap().entrySet()) {
ObjectPool pool = entry.getValue();
String poolName = entry.getKey();
buffer.append("* Server pool=").append(poolName == null ? "default pool" : poolName)
.append("\n").append(" - pool active Size=").append(pool.getNumActive());
buffer.append(", pool Idle size=").append(pool.getNumIdle()).append(StringUtil.LINE_SEPARATOR);
}
}
static class CloseObjectPoolHeartbeatDelayed extends HeartbeatDelayed {
private ObjectPool pool;
public CloseObjectPoolHeartbeatDelayed(long nsTime, TimeUnit timeUnit, ObjectPool pool) {
super(nsTime, timeUnit);
this.pool = pool;
}
@Override
public Status doCheck() {
if (pool.getNumActive() == 0) {
return Status.VALID;
}
return null;
}
public boolean isCycle() {
return false;
}
public void cancel() {
try {
this.pool.close();
} catch (Exception e) {
// TODO handle exception
logger.warn("close pool error", e);
}
}
public boolean equals(Object obj) {
if (obj instanceof CloseObjectPoolHeartbeatDelayed) {
CloseObjectPoolHeartbeatDelayed other = (CloseObjectPoolHeartbeatDelayed) obj;
return other.pool == this.pool && this.getClass() == obj.getClass();
} else {
return false;
}
}
public int hashCode() {
return pool == null ? this.getClass().hashCode() : this.getClass().hashCode()
+ pool.hashCode();
}
@Override
public String getName() {
return "closing Pool=" + pool.getName();
}
}
private ObjectPool createObjectPool(DBServerConfig dbServerConfig)
throws UpdateDBServerRuntimeException {
ObjectPool pool = null;
try {
BeanObjectEntityConfig poolConfig = dbServerConfig.getPoolConfig();
pool = (ObjectPool) createBeanObjectEntity(poolConfig, true);
pool.setName(StringUtil.isEmpty(dbServerConfig.getName())
? poolConfig.getName()
: dbServerConfig.getName());
if (dbServerConfig.getFactoryConfig() != null && !dbServerConfig.getVirtual()) {
PoolableObjectFactory factory =
(PoolableObjectFactory) dbServerConfig.getFactoryConfig().createBeanObject(true,
conMgrMap);
pool.setFactory(factory);
}
} catch (Exception e) {
String poolName = dbServerConfig.getName();
String msg = String.format("Create pool %s bean error", poolName);
logger.error(msg, e);
throw new UpdateDBServerRuntimeException(poolName, msg, e);
}
if (pool instanceof MultipleLoadBalanceObjectPool) {
MultipleLoadBalanceObjectPool multiPool = (MultipleLoadBalanceObjectPool) pool;
multiPool.initAllPools();
} else {
PoolableObject object = null;
try {
object = (PoolableObject) pool.borrowObject();
} catch (Exception e) {
String poolName = pool.getName();
String msg = String.format("init pool %s error", poolName);
logger.error(msg, e);
throw new UpdateDBServerRuntimeException(poolName, msg, e);
} finally {
if (object != null) {
try {
pool.returnObject(object);
} catch (Exception e) {
String poolName = pool.getName();
String msg = String.format("return init pool %s error", poolName);
logger.error(msg, e);
throw new UpdateDBServerRuntimeException(poolName, msg, e);
}
}
}
}
return pool;
}
public void closePool(ObjectPool pool) {
if (pool != null) {
try {
pool.close();
} catch (Exception e) {
String poolName = pool.getName();
String msg = String.format("close pool %s error", poolName);
throw new UpdateDBServerRuntimeException(poolName, msg, e);
}
}
}
/**
* 更新单个DB Server
* 捕获所有异常,然后简单跳过异常,这样可以在批量更新多个实例的时候,出错的实例不影响正常的实例
*
* @param sourceConfig 单个db server 配置
* @param tryUpdate 是否是真实更新或是只是尝试更新
* @param dbServers 内存(缓存) 中的db server 配置映射
* @param poolMap 内存(缓存) 中的pool 配置映射
* @param ctxChangedListeners 内存(缓存)中的ConetextListner列表
* @throws ConfigurationException
*/
public void updateDBServer(DBServerConfig sourceConfig, boolean tryUpdate,
Map<String, DBServerConfig> dbServers, Map<String, ObjectPool> poolMap,
List<ContextChangedListener> ctxChangedListeners) {
try {
if (sourceConfig == null) {
throw new ConfigurationException("config cannot be null");
}
boolean abstractive = sourceConfig.getAbstractive();
// 尝试更新,并不做实际操作
if (tryUpdate) {
// try to create ObjectPool with this sourceConfig
if (!abstractive) {
DBServerConfig config = (DBServerConfig) sourceConfig.clone();
if (sourceConfig.getParent() != null) {
DBServerConfig parent = dbServers.get(config.getParent());
if (parent == null || !parent.isEnable()) {
throw new ConfigurationException("parent config withe name=" + config.getParent()
+ " not found or disable");
}
this.inheritDBServerConfig(parent, config);
}
if (sourceConfig.isEnable()) {
ObjectPool pool = createObjectPool(config);
closePool(pool);
} else {
dbServers.remove(sourceConfig.getName());
}
} else {
if (sourceConfig.isEnable()) {
for (Map.Entry<String, DBServerConfig> entry : dbServers.entrySet()) {
if (StringUtil.equals(entry.getValue().getParent(), sourceConfig.getName())) {
if (!entry.getValue().getAbstractive()) {
DBServerConfig child = (DBServerConfig) entry.getValue().clone();
this.inheritDBServerConfig(sourceConfig, child);
ObjectPool pool = createObjectPool(child);
closePool(pool);
break;
}
}
}
}
}
}
// 实际更新,重建连接,刷新缓存map
else {
/**
* close old objectPool if this configuration is abstractive then close all children's
* objectPools else the old ObjectPool will be closed
*
*/
if (!abstractive) {
DBServerConfig config = (DBServerConfig) sourceConfig.clone();
if (sourceConfig.getParent() != null) {
DBServerConfig parent = dbServers.get(sourceConfig.getParent());
if (parent == null || !parent.isEnable()) {
throw new ConfigurationException("parent config withe name=" + sourceConfig.getParent()
+ " not found or disable");
}
this.inheritDBServerConfig(parent, config);
}
// close old ObjectPool
ObjectPool oldObjectPool = poolMap.get(sourceConfig.getName());
if (sourceConfig.isEnable()) {
ObjectPool pool = createObjectPool(config);
if (pool != null) {
poolMap.put(sourceConfig.getName(), pool);
}
// 新增了一个ContextChangedListener
if (oldObjectPool == null) {
if (pool instanceof ContextChangedListener) {
ctxChangedListeners.add((ContextChangedListener) pool);
}
}
} else {
dbServers.remove(sourceConfig.getName());
poolMap.remove(sourceConfig.getName());
// 删除了一个ContextChangedListener
if (oldObjectPool != null) {
if (oldObjectPool instanceof ContextChangedListener) {
ctxChangedListeners.remove((ContextChangedListener) oldObjectPool);
}
}
}
if (oldObjectPool != null) {
closePool(oldObjectPool);
}
}
// update abstractive pool
else {
for (Map.Entry<String, DBServerConfig> entry : dbServers.entrySet()) {
if (StringUtil.equals(entry.getValue().getParent(), sourceConfig.getName())) {
if (!entry.getValue().getAbstractive()) {
DBServerConfig child = (DBServerConfig) entry.getValue().clone();
this.inheritDBServerConfig(sourceConfig, child);
// close all children's ObjectPools
ObjectPool oldObjectPool = poolMap.get(child.getName());
if (sourceConfig.isEnable()) {
ObjectPool pool = createObjectPool(child);
if (pool != null) {
poolMap.put(child.getName(), pool);
}
// 新增了一个ContextChangedListener
if (oldObjectPool == null) {
if (pool instanceof ContextChangedListener) {
ctxChangedListeners.add((ContextChangedListener) pool);
}
}
} else {
dbServers.remove(child.getName());
poolMap.remove(child.getName());
// 删除了一个ContextChangedListener
if (oldObjectPool != null) {
if (oldObjectPool instanceof ContextChangedListener) {
ctxChangedListeners.remove((ContextChangedListener) oldObjectPool);
}
}
}
if (oldObjectPool != null) {
closePool(oldObjectPool);
}
}
}
}
if (!sourceConfig.isEnable()) {
dbServers.remove(sourceConfig.getName());
poolMap.remove(sourceConfig.getName());
}
}
}
} catch (Exception e) {
String poolName = sourceConfig == null ? "unknown" : sourceConfig.getName();
logger.error(String.format("update db pool name=%s failed since %s", poolName, e.getMessage()));
}
}
}