/** * <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.context; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.helpers.LogLog; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import com.meidusa.amoeba.config.BeanObjectEntityConfig; import com.meidusa.amoeba.config.ConfigurationException; import com.meidusa.amoeba.config.DBServerConfig; import com.meidusa.amoeba.config.DocumentUtil; import com.meidusa.amoeba.config.ParameterMapping; import com.meidusa.amoeba.config.ProxyServerConfig; 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.net.poolable.PoolableObjectFactory; import com.meidusa.amoeba.route.QueryRouter; import com.meidusa.amoeba.util.Initialisable; import com.meidusa.amoeba.util.InitialisationException; import com.meidusa.amoeba.util.Reporter; import com.meidusa.amoeba.util.StringUtil; /** * Amoeba����ʱ��һЩ��Ϣ��context��������amoeba.xml�е�������Ϣ�ȡ��������� * @author <a href=mailto:piratebase@sina.com>Struct chen</a> */ public abstract 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; private ProxyServerConfig config; private Executor readExecutor; private Executor clientSideExecutor; private Executor serverSideExecutor; private Map<String, ConnectionManager> conMgrMap = new HashMap<String, ConnectionManager>(); private Map<String, ConnectionManager> readOnlyConMgrMap = Collections.unmodifiableMap(conMgrMap); private Map<String, ObjectPool> poolMap = new HashMap<String, ObjectPool>(); private Map<String, ObjectPool> readOnlyPoolMap = Collections.unmodifiableMap(poolMap); private QueryRouter queryRouter; private String serverCharset; /** * ����context��ֵ * @return context */ public static ProxyRuntimeContext getInstance() { return context; } /** * ���캯��������Ա����context��ֵ * @param context */ protected static void setInstance(ProxyRuntimeContext context) { ProxyRuntimeContext.context = context; } /** * ������ * @return */ protected abstract String getDefaultServerConnectionFactoryClassName(); /** * ����DEFAULT_SERVER_CONNECTION_MANAGER_CLASS��ֵ * @return "com.meidusa.amoeba.net.AuthingableConnectionManager" */ protected String getDefaultServerConnectionManagerClassName() { return DEFAULT_SERVER_CONNECTION_MANAGER_CLASS; } /** * ���� DEFAULT_REAL_POOL_CLASS��ֵ * @return "com.meidusa.amoeba.net.poolable.PoolableObjectPool" */ protected String getDefaultRealPoolClassName() { return DEFAULT_REAL_POOL_CLASS; } /** * ����DEFAULT_VIRTUAL_POOL_CLASS��ֵ * @return "com.meidusa.amoeba.server.MultipleServerPool" */ protected String getDefaultVirtualPoolClassName() { return DEFAULT_VIRTUAL_POOL_CLASS; } /** * ���������ַ���serverCharset * @return serverCharset */ public String getServerCharset() { return serverCharset; } /** * ����Ա����serverCharset��ֵ * @param serverCharset */ public void setServerCharset(String serverCharset) { this.serverCharset = serverCharset; } /** * ����config��ֵ��config�б��ֵ���amoeba.xml��ȫ����Ϣ������ip��ipaddress port,dbserver,server1,connectionManagerList�ȵ� * @return config */ public ProxyServerConfig getConfig() { return config; } /** * * @return queryRouter */ public QueryRouter getQueryRouter() { return queryRouter; } static class ReNameableThreadExecutor extends ThreadPoolExecutor { // Map<Thread,String> threadNameMap = new HashMap<Thread,String>(); /** * * @param poolSize Ʃ��amoeba.xml server����readThreadPoolSize��ֵ */ public ReNameableThreadExecutor(int poolSize){ super(poolSize, poolSize, Long.MAX_VALUE, TimeUnit.NANOSECONDS, new LinkedBlockingQueue<Runnable>()); } // protected void beforeExecute(Thread t, Runnable r) { // if (r instanceof NameableRunner) { // NameableRunner nameableRunner = (NameableRunner) r; // String name = t.getName(); // if (name != null) { // threadNameMap.put(t, t.getName()); // t.setName(nameableRunner.getRunnerName() + ":" + t.getName()); // } // } // }; // // protected void afterExecute(Runnable r, Throwable t) { // if (r instanceof NameableRunner) { // String name = threadNameMap.remove(Thread.currentThread()); // if (name != null) { // Thread.currentThread().setName(name); // } // } // }; } protected ProxyRuntimeContext(){ } public Map<String, ConnectionManager> getConnectionManagerList() { return readOnlyConMgrMap; } public Executor getClientSideExecutor() { return clientSideExecutor; } public Executor getReadExecutor() { return readExecutor; } public Executor getServerSideExecutor() { return serverSideExecutor; } public Map<String, ObjectPool> getPoolMap() { return readOnlyPoolMap; } private List<Initialisable> initialisableList = new ArrayList<Initialisable>(); /** * * @param file */ public void init(String file) { config = loadConfig(file);//Ʃ��fileָ������amoeba.xml,config�������amoeba.xml�е�ȫ����Ϣ readExecutor = new ReNameableThreadExecutor(config.getReadThreadPoolSize()); serverSideExecutor = new ReNameableThreadExecutor(config.getServerSideThreadPoolSize()); clientSideExecutor = new ReNameableThreadExecutor(config.getClientSideThreadPoolSize()); serverCharset = config.getServerCharset(); /** * �²⣺config�б��ֵ���amoeba.xml�е�ȫ��������Ϣ���������for�Ǹ���connectionManagerList������Ӧ���� */ 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); } } /** * config�б�����amoeba.xml��ȫ��������Ϣ���������for�Ǹ���dbServerList�е���Ϣ������Ӧ�Ĵ��� */ for (Map.Entry<String, DBServerConfig> entry : config.getDbServers().entrySet()) { DBServerConfig dbServerConfig = entry.getValue(); try { BeanObjectEntityConfig poolConfig = dbServerConfig.getPoolConfig(); ObjectPool pool = (ObjectPool) poolConfig.createBeanObject(false); if (pool instanceof Initialisable) { initialisableList.add((Initialisable) pool); } if (dbServerConfig.getFactoryConfig() != null) { PoolableObjectFactory factory = (PoolableObjectFactory) dbServerConfig.getFactoryConfig().createBeanObject(false); if (factory instanceof Initialisable) { initialisableList.add((Initialisable) factory); } pool.setFactory(factory); } poolMap.put(entry.getKey(), pool); } catch (Exception e) { // throw new ConfigurationException("manager instance error", e);//ԭ����������׳��쳣�����,���Ե�ʱ��ᱨ���Ͱ���ע�͵��� } } //config�б�����amoeba.xml��ȫ��������Ϣ���������for�Ǹ���queryRouter�е���Ϣ������Ӧ�Ĵ��� if (config.getQueryRouterConfig() != null) { BeanObjectEntityConfig queryRouterConfig = config.getQueryRouterConfig(); try { queryRouter = (QueryRouter) queryRouterConfig.createBeanObject(false); if (queryRouter instanceof Initialisable) { initialisableList.add((Initialisable) queryRouter); } } catch (Exception e) { // throw new ConfigurationException("queryRouter instance error", e);//ԭ����������׳��쳣�����,���Ե�ʱ��ᱨ���Ͱ���ע�͵��� } } initAllInitialisableBeans(); initialisableList.clear(); for (ConnectionManager cm : getConnectionManagerList().values()) { cm.setExecutor(this.getReadExecutor()); cm.start(); } initPools(); } 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 error!", e); } finally { if (object != null) { try { pool.returnObject(object); } catch (Exception e) { logger.error("return init pools error", e); } } } } } } /** * initialisableList�б��ֵ���amoeba.xml��������Ϣ����һ����ʽ����ǰ����ʽ��config�� * ��initialisableList�е���Ϣ��ӵ�bean�� */ private void initAllInitialisableBeans() { for (Initialisable bean : initialisableList) { try { bean.init(); } catch (InitialisationException e) { throw new ConfigurationException("Initialisation Exception", e); } } } /** * ����amoeba.dtd,��ʵ���ǽ���amoeba.xml * configFileName = "F:\\project\\Amoeba\\Aladdin\\conf\\amoeba.xml" * @param configFileName * @return �������amoeba.xml�е�ȫ����Ϣ��ProxyServerConfig���ͣ� */ private ProxyServerConfig loadConfig(String configFileName) { DocumentBuilder db; // System.out.println(configFileName); try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setValidating(true); dbf.setNamespaceAware(false); db = dbf.newDocumentBuilder(); db.setEntityResolver(new EntityResolver() { public InputSource resolveEntity(String publicId, String systemId) { if (systemId.endsWith("amoeba.dtd")) { InputStream in = ProxyRuntimeContext.class.getResourceAsStream("/com/meidusa/amoeba/xml/amoeba.dtd"); if (in == null) { LogLog.error("Could not find [amoeba.dtd]. Used [" + ProxyRuntimeContext.class.getClassLoader() + "] class loader in the search."); return null; } else { return new InputSource(in); } } else { return null; } } }); db.setErrorHandler(new ErrorHandler() { public void warning(SAXParseException exception) { } public void error(SAXParseException exception) throws SAXException { logger.error(exception.getMessage() + " at (" + exception.getLineNumber() + ":" + exception.getColumnNumber() + ")"); throw exception; } public void fatalError(SAXParseException exception) throws SAXException { logger.fatal(exception.getMessage() + " at (" + exception.getLineNumber() + ":" + exception.getColumnNumber() + ")"); throw exception; } }); // configFileName = "F:\\project\\Amoeba\\Aladdin\\conf\\amoeba.xml" return loadConfigurationFile(configFileName, db); } catch (Exception e) { logger.fatal("Could not load configuration file, failing", e); throw new ConfigurationException("Error loading configuration file " + configFileName, e); } } /** * ����amoeba.xml * @param fileName ���� "F:\\project\\Amoeba\\Aladdin\\conf\\amoeba.xml" * @param db * @return config �������amoeba.xml�е�ȫ����Ϣ */ private ProxyServerConfig loadConfigurationFile(String fileName, DocumentBuilder db) { Document doc = null; InputStream is = null; ProxyServerConfig config = new ProxyServerConfig(); try { is = new FileInputStream(new File(fileName)); if (is == null) { throw new Exception("Could not open file " + fileName); } doc = db.parse(is); } catch (Exception e) { final String s = "Caught exception while loading file " + fileName; logger.error(s, e); throw new ConfigurationException(s, e); } finally { if (is != null) { try { is.close(); } catch (IOException e) { logger.error("Unable to close input stream", e); } } } Element rootElement = doc.getDocumentElement(); NodeList children = rootElement.getChildNodes(); int childSize = children.getLength(); for (int i = 0; i < childSize; i++) { Node childNode = children.item(i); if (childNode instanceof Element) { Element child = (Element) childNode; final String nodeName = child.getNodeName(); if (nodeName.equals("server")) { // System.out.println(child.getChildNodes()); loadServerConfig(child, config); } else if (nodeName.equals("connectionManagerList")) { loadConnectionManagers(child, config); } else if (nodeName.equals("dbServerList")) { loadServers(child, config); } else if (nodeName.equals("queryRouter")) { loadQueryRouter(rootElement, config); } } } if (logger.isInfoEnabled()) { logger.info("Loaded Engine configuration from: " + fileName); } return config; } /** * ����amoeba.xml��queryRouter�����Ϣ * @param current * @param config */ private void loadQueryRouter(Element current, ProxyServerConfig config) { BeanObjectEntityConfig queryRouter = DocumentUtil.loadBeanConfig(DocumentUtil.getTheOnlyElement(current, "queryRouter")); config.setQueryRouterConfig(queryRouter); } /** * ����amoeba.xml��dbServerList�����Ϣ * @param current * @param config * @author Li Hui */ private void loadServers(Element current, ProxyServerConfig config) { NodeList children = current.getChildNodes(); int childSize = children.getLength(); for (int i = 0; i < childSize; i++) { Node childNode = children.item(i); if (childNode instanceof Element) { Element child = (Element) childNode; DBServerConfig serverConfig = loadServer(child); if (serverConfig.isVirtual()) { if (serverConfig.getPoolConfig() != null) { if (StringUtil.isEmpty(serverConfig.getPoolConfig().getClassName())) { serverConfig.getPoolConfig().setClassName(getDefaultVirtualPoolClassName()); } } } else { if (serverConfig.getPoolConfig() != null) { if (StringUtil.isEmpty(serverConfig.getPoolConfig().getClassName())) { serverConfig.getPoolConfig().setClassName(getDefaultRealPoolClassName()); } } } if (serverConfig.getFactoryConfig() != null) { if (StringUtil.isEmpty(serverConfig.getFactoryConfig().getClassName())) { serverConfig.getFactoryConfig().setClassName(getDefaultServerConnectionFactoryClassName()); } } /** * ��ÿһ��dbServer��Ʃ��dbServer1�ڵ��е���Ϣ������Ϣ���浽serverConfig�� */ config.addServer(serverConfig.getName(), serverConfig);//���configΪ(dbServer1,dbServer1�е�ȫ����Ϣ�� } } } /** * ����amoeba.xml��dbServerList�����dbServer�ӽڵ���Ϣ * @param current * @return */ private DBServerConfig loadServer(Element current) { DBServerConfig serverConfig = new DBServerConfig(); NamedNodeMap nodeMap = current.getAttributes(); Map<String, Object> map = new HashMap<String, Object>(); for (int i = 0; i < nodeMap.getLength(); i++) { Node node = nodeMap.item(i); if (node instanceof org.w3c.dom.Attr) { Attr attr = (Attr) node; map.put(attr.getName(), attr.getNodeValue()); } } ParameterMapping.mappingObject(serverConfig, map); BeanObjectEntityConfig factory = DocumentUtil.loadBeanConfig(DocumentUtil.getTheOnlyElement(current, "factoryConfig")); BeanObjectEntityConfig pool = DocumentUtil.loadBeanConfig(DocumentUtil.getTheOnlyElement(current, "poolConfig")); if (pool != null) { serverConfig.setPoolConfig(pool); } if (factory != null) { serverConfig.setFactoryConfig(factory); } return serverConfig; } /** * ����amoeba.xml��connectionManagerList�����Ϣ * ͨ�����ԣ�Ŀǰ��û�з�����ʲô�������� * @param current * @param config */ private void loadConnectionManagers(Element current, ProxyServerConfig config) { NodeList children = current.getChildNodes(); int childSize = children.getLength(); for (int i = 0; i < childSize; i++) { Node childNode = children.item(i); if (childNode instanceof Element) { Element child = (Element) childNode; BeanObjectEntityConfig managerConfig = DocumentUtil.loadBeanConfig(child); if (StringUtil.isEmpty(managerConfig.getClassName())) { managerConfig.setClassName(getDefaultServerConnectionManagerClassName()); } config.addManager(managerConfig.getName(), managerConfig); } } } /** * ��conf/amoeba.xml��server����Ӧ����Ϣ��ӵ�map�� * @param current * @param config */ private void loadServerConfig(Element current, ProxyServerConfig config) { NodeList children = current.getChildNodes(); int childSize = children.getLength(); Map<String, Object> map = new HashMap<String, Object>(); for (int i = 0; i < childSize; i++) { Node childNode = children.item(i); if (childNode instanceof Element) { Element child = (Element) childNode; final String nodeName = child.getNodeName(); if (nodeName.equals("property")) { String key = child.getAttribute("name"); String value = child.getTextContent(); map.put(key, value); } } } ParameterMapping.mappingObject(config, map); } /** * ��һЩlog��Ϣ���뵽project.log�ļ��� */ public void appendReport(StringBuilder buffer, long now, long sinceLast, boolean reset, Level level) { /** * ��log�����project.log�ļ��У����ɵĴ�����������������ӵ� * * Server pool=server1 - pool active Size=0, pool Idle size=1 * Server pool=server2 - pool active Size=0, pool Idle size=1 * Server pool=multiPool - pool active Size=0, pool Idle size=2 * Server pool=_null_table_ - pool active Size=0, pool Idle size=1 */ 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("\n"); } } }