/** * <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.route; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.commons.collections.map.LRUMap; import org.apache.log4j.Logger; import org.apache.log4j.helpers.LogLog; import org.w3c.dom.Document; import org.w3c.dom.Element; 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.DocumentUtil; import com.meidusa.amoeba.context.ProxyRuntimeContext; import com.meidusa.amoeba.net.DatabaseConnection; import com.meidusa.amoeba.net.poolable.ObjectPool; import com.meidusa.amoeba.parser.ParseException; import com.meidusa.amoeba.parser.Parser; import com.meidusa.amoeba.parser.dbobject.Column; import com.meidusa.amoeba.parser.dbobject.Schema; import com.meidusa.amoeba.parser.dbobject.Table; import com.meidusa.amoeba.parser.function.Function; import com.meidusa.amoeba.parser.statment.CommitStatment; import com.meidusa.amoeba.parser.statment.DMLStatment; import com.meidusa.amoeba.parser.statment.PropertyStatment; import com.meidusa.amoeba.parser.statment.RollbackStatment; import com.meidusa.amoeba.parser.statment.ShowStatment; import com.meidusa.amoeba.parser.statment.StartTansactionStatment; import com.meidusa.amoeba.parser.statment.Statment; import com.meidusa.amoeba.sqljep.RowJEP; import com.meidusa.amoeba.sqljep.function.Abs; import com.meidusa.amoeba.sqljep.function.AddDate; import com.meidusa.amoeba.sqljep.function.AddMonths; import com.meidusa.amoeba.sqljep.function.AddTime; import com.meidusa.amoeba.sqljep.function.Case; import com.meidusa.amoeba.sqljep.function.Ceil; import com.meidusa.amoeba.sqljep.function.Comparative; import com.meidusa.amoeba.sqljep.function.ComparativeBaseList; import com.meidusa.amoeba.sqljep.function.Concat; import com.meidusa.amoeba.sqljep.function.Datediff; import com.meidusa.amoeba.sqljep.function.Day; import com.meidusa.amoeba.sqljep.function.DayName; import com.meidusa.amoeba.sqljep.function.DayOfWeek; import com.meidusa.amoeba.sqljep.function.DayOfYear; import com.meidusa.amoeba.sqljep.function.Decode; import com.meidusa.amoeba.sqljep.function.Floor; import com.meidusa.amoeba.sqljep.function.Hash; import com.meidusa.amoeba.sqljep.function.Hour; import com.meidusa.amoeba.sqljep.function.IndistinctMatching; import com.meidusa.amoeba.sqljep.function.Initcap; import com.meidusa.amoeba.sqljep.function.Instr; import com.meidusa.amoeba.sqljep.function.LastDay; import com.meidusa.amoeba.sqljep.function.Length; import com.meidusa.amoeba.sqljep.function.Lower; import com.meidusa.amoeba.sqljep.function.Lpad; import com.meidusa.amoeba.sqljep.function.Ltrim; import com.meidusa.amoeba.sqljep.function.MakeDate; import com.meidusa.amoeba.sqljep.function.MakeTime; import com.meidusa.amoeba.sqljep.function.Microsecond; import com.meidusa.amoeba.sqljep.function.Minute; import com.meidusa.amoeba.sqljep.function.Modulus; import com.meidusa.amoeba.sqljep.function.Month; import com.meidusa.amoeba.sqljep.function.MonthName; import com.meidusa.amoeba.sqljep.function.MonthsBetween; import com.meidusa.amoeba.sqljep.function.NextDay; import com.meidusa.amoeba.sqljep.function.Nvl; import com.meidusa.amoeba.sqljep.function.PostfixCommand; import com.meidusa.amoeba.sqljep.function.Power; import com.meidusa.amoeba.sqljep.function.Replace; import com.meidusa.amoeba.sqljep.function.Round; import com.meidusa.amoeba.sqljep.function.Rpad; import com.meidusa.amoeba.sqljep.function.Rtrim; import com.meidusa.amoeba.sqljep.function.Second; import com.meidusa.amoeba.sqljep.function.Sign; import com.meidusa.amoeba.sqljep.function.SubDate; import com.meidusa.amoeba.sqljep.function.SubTime; import com.meidusa.amoeba.sqljep.function.Substring; import com.meidusa.amoeba.sqljep.function.ToChar; import com.meidusa.amoeba.sqljep.function.ToDate; import com.meidusa.amoeba.sqljep.function.ToNumber; import com.meidusa.amoeba.sqljep.function.Translate; import com.meidusa.amoeba.sqljep.function.Trim; import com.meidusa.amoeba.sqljep.function.Trunc; import com.meidusa.amoeba.sqljep.function.Upper; import com.meidusa.amoeba.sqljep.function.WeekOfYear; import com.meidusa.amoeba.sqljep.function.Year; import com.meidusa.amoeba.sqljep.variable.Variable; import com.meidusa.amoeba.util.Initialisable; import com.meidusa.amoeba.util.InitialisationException; import com.meidusa.amoeba.util.StringUtil; /** * @author struct * @author hexianmao */ @SuppressWarnings("deprecation") public abstract class AbstractQueryRouter implements QueryRouter, Initialisable { private static Logger logger = Logger.getLogger(AbstractQueryRouter.class); public final static Map<String, PostfixCommand> ruleFunTab = new HashMap<String, PostfixCommand>(); static { ruleFunTab.put("abs", new Abs()); ruleFunTab.put("power", new Power()); ruleFunTab.put("mod", new Modulus()); ruleFunTab.put("substr", new Substring()); ruleFunTab.put("sign", new Sign()); ruleFunTab.put("ceil", new Ceil()); ruleFunTab.put("floor", new Floor()); ruleFunTab.put("trunc", new Trunc()); ruleFunTab.put("round", new Round()); ruleFunTab.put("length", new Length()); ruleFunTab.put("concat", new Concat()); ruleFunTab.put("instr", new Instr()); ruleFunTab.put("trim", new Trim()); ruleFunTab.put("rtrim", new Rtrim()); ruleFunTab.put("ltrim", new Ltrim()); ruleFunTab.put("rpad", new Rpad()); ruleFunTab.put("lpad", new Lpad()); ruleFunTab.put("lower", new Lower()); ruleFunTab.put("upper", new Upper()); ruleFunTab.put("translate", new Translate()); ruleFunTab.put("replace", new Replace()); ruleFunTab.put("initcap", new Initcap()); ruleFunTab.put("value", new Nvl()); ruleFunTab.put("decode", new Decode()); ruleFunTab.put("to_char", new ToChar()); ruleFunTab.put("to_number", new ToNumber()); ruleFunTab.put("imatch", new IndistinctMatching()); // replacement for of Oracle's SOUNDEX ruleFunTab.put("months_between", new MonthsBetween()); ruleFunTab.put("add_months", new AddMonths()); ruleFunTab.put("last_day", new LastDay()); ruleFunTab.put("next_day", new NextDay()); ruleFunTab.put("to_date", new ToDate()); ruleFunTab.put("case", new Case()); // replacement for CASE WHEN digit = 0 THEN ...;WHEN digit = 1 // THEN...;ELSE... END CASE ruleFunTab.put("index", new Instr()); // maxdb ruleFunTab.put("num", new ToNumber()); // maxdb ruleFunTab.put("chr", new ToChar()); // maxdb ruleFunTab.put("dayname", new DayName()); // maxdb ruleFunTab.put("adddate", new AddDate()); // maxdb ruleFunTab.put("subdate", new SubDate()); // maxdb ruleFunTab.put("addtime", new AddTime()); // maxdb ruleFunTab.put("subtime", new SubTime()); // maxdb ruleFunTab.put("year", new Year()); // maxdb ruleFunTab.put("month", new Month()); // maxdb ruleFunTab.put("day", new Day()); // maxdb ruleFunTab.put("dayofmonth", new Day()); // maxdb ruleFunTab.put("hour", new Hour()); // maxdb ruleFunTab.put("minute", new Minute()); // maxdb ruleFunTab.put("second", new Second()); // maxdb ruleFunTab.put("microsecond", new Microsecond()); // maxdb ruleFunTab.put("datediff", new Datediff()); // maxdb ruleFunTab.put("dayofweek", new DayOfWeek()); // maxdb ruleFunTab.put("weekofyear", new WeekOfYear()); // maxdb ruleFunTab.put("dayofyear", new DayOfYear()); // maxdb ruleFunTab.put("dayname", new DayName()); // maxdb ruleFunTab.put("monthname", new MonthName()); // maxdb ruleFunTab.put("makedate", new MakeDate()); // maxdb ruleFunTab.put("maketime", new MakeTime()); // maxdb ruleFunTab.put("hash", new Hash()); // } /* Ĭ��1000 */ private int LRUMapSize = 1000; private LRUMap map; private Lock mapLock = new ReentrantLock(false); private Map<Table, TableRule> tableRuleMap = new HashMap<Table, TableRule>(); private Map<String, Function> functionMap = new HashMap<String, Function>(); private Map<String, PostfixCommand> ruleFunctionMap = new HashMap<String, PostfixCommand>(); protected ObjectPool[] defaultPools; protected ObjectPool[] readPools; protected ObjectPool[] writePools; private String ruleConfig; private String functionConfig; private String ruleFunctionConfig; private String defaultPool; private String readPool; private String writePool; private boolean needParse = true; private boolean needEvaluate = true; public AbstractQueryRouter(){ ruleFunctionMap.putAll(ruleFunTab); } public String getRuleConfig() { return ruleConfig; } public abstract Parser newParser(String sql); public void setRuleConfig(String ruleConfig) { this.ruleConfig = ruleConfig; } public void setReadPool(String readPool) { this.readPool = readPool; } public String getReadPool() { return readPool; } public String getWritePool() { return writePool; } public void setWritePool(String writePool) { this.writePool = writePool; } public ObjectPool[] doRoute(DatabaseConnection connection, String sql, boolean ispreparedStatment, Object[] parameters) { if (sql == null) { return defaultPools; } if (needParse) { return selectPool(connection, sql, ispreparedStatment, parameters); } else { return defaultPools; } } /** * ����Query ��route��Ŀ���ַ ObjectPool���� �������null����������DatabaseConnection �����������õ����� */ protected ObjectPool[] selectPool(DatabaseConnection connection, String sql, boolean ispreparedStatment, Object[] parameters) { List<String> poolNames = new ArrayList<String>(); Statment statment = parseSql(connection, sql); DMLStatment dmlStatment = null; if (statment instanceof DMLStatment) { if (logger.isDebugEnabled()) { logger.debug("DMLStatement:[" + sql + "] Expression=[" + statment.getExpression() + "]"); } dmlStatment = (DMLStatment) statment; Map<Table, Map<Column, Comparative>> tables = null; if (needEvaluate) { tables = dmlStatment.evaluate(parameters); if (tables != null && tables.size() > 0) { Set<Map.Entry<Table, Map<Column, Comparative>>> entrySet = tables.entrySet(); for (Map.Entry<Table, Map<Column, Comparative>> entry : entrySet) { Map<Column, Comparative> columnMap = entry.getValue(); TableRule tableRule = this.tableRuleMap.get(entry.getKey()); // �������table Rule ����Ҫ���Ƿ���Rule if (tableRule != null) { // û���е�sql��䣬ʹ��Ĭ�ϵ�tableRule if (columnMap == null || ispreparedStatment) { String[] pools = dmlStatment.isReadStatment() ? tableRule.readPools : tableRule.writePools; if (pools == null || pools.length == 0) { pools = tableRule.defaultPools; } for (String poolName : pools) { if (!poolNames.contains(poolName)) { poolNames.add(poolName); } } if (logger.isDebugEnabled()) { logger.debug("[" + sql + "] no Column rule, using table:" + tableRule.table + " default rules:" + Arrays.toString(tableRule.defaultPools)); } continue; } List<String> groupMatched = new ArrayList<String>(); for (Rule rule : tableRule.ruleList) { if (rule.group != null) { if (groupMatched.contains(rule.group)) { continue; } } // ��������ȱ���IJ���С���������һ������ if (columnMap.size() < rule.parameterMap.size()) { continue; } else { boolean matched = true; // �����ѯ����а����˸ù�����Ҫ�IJ�������ù��򽫱����� for (Column exclude : rule.excludes) { Comparable<?> condition = columnMap.get(exclude); if (condition != null) { matched = false; break; } } // �����ƥ�佫������һ������ if (!matched) { continue; } Comparable<?>[] comparables = new Comparable[rule.parameterMap.size()]; // �����еIJ���������dmlstatement�д��ڣ�����������򽫲������� for (Map.Entry<Column, Integer> parameter : rule.cloumnMap.entrySet()) { Comparative condition = columnMap.get(parameter.getKey()); if (condition != null) { // ���������� ����� ���������Ҳ�����array ����������Ըù��� if (rule.ignoreArray && condition instanceof ComparativeBaseList) { matched = false; break; } comparables[parameter.getValue()] = (Comparative) condition.clone(); } else { matched = false; break; } } // �����ƥ�佫������һ������ if (!matched) { continue; } try { Comparable<?> result = rule.rowJep.getValue(comparables); Integer i = 0; if (result instanceof Comparative) { if (rule.isSwitch) { i = (Integer) ((Comparative) result).getValue(); if (i < 0) { continue; } matched = true; } else { matched = (Boolean) ((Comparative) result).getValue(); } } else { matched = (Boolean) result; } if (matched) { if (rule.group != null) { groupMatched.add(rule.group); } String[] pools = dmlStatment.isReadStatment() ? rule.readPools : rule.writePools; if (pools == null || pools.length == 0) { pools = rule.defaultPools; } if (pools != null && pools.length > 0) { if (rule.isSwitch) { if (!poolNames.contains(pools[i])) { poolNames.add(pools[i]); } } else { for (String poolName : pools) { if (!poolNames.contains(poolName)) { poolNames.add(poolName); } } } } else { logger.error("rule:" + rule.name + " matched, but pools is null"); } if (logger.isDebugEnabled()) { logger.debug("[" + sql + "] matched table:" + tableRule.table.getName() + ", rule:" + rule.name); } } } catch (com.meidusa.amoeba.sqljep.ParseException e) { // logger.error("parse rule error:"+rule.expression,e); } } } // ������й����޷�ƥ�䣬��Ĭ�ϲ���TableRule�е�pool���á� if (poolNames.size() == 0) { String[] pools = dmlStatment.isReadStatment() ? tableRule.readPools : tableRule.writePools; if (pools == null || pools.length == 0) { pools = tableRule.defaultPools; } logger.warn("no rule matched, using tableRule:[" + tableRule.table.getName() + "] defaultPools"); for (String poolName : pools) { if (!poolNames.contains(poolName)) { poolNames.add(poolName); } } } } } } } } else if (statment instanceof PropertyStatment) { if (logger.isDebugEnabled()) { logger.debug("PropertyStatment:[" + sql + "]"); } setProperty(connection, (PropertyStatment) statment, parameters); return null; } else if (statment instanceof ShowStatment) { if (logger.isDebugEnabled()) { logger.debug("ShowStatment:[" + sql + "]"); } TableRule tableRule = this.tableRuleMap.get(null); if (tableRule != null && tableRule.defaultPools != null && tableRule.defaultPools.length > 0) { for (String poolName : tableRule.defaultPools) { if (!poolNames.contains(poolName)) { poolNames.add(poolName); } } } } else if (statment instanceof StartTansactionStatment) { if (logger.isDebugEnabled()) { logger.debug("StartTansactionStatment:[" + sql + "]"); } return null; } else if (statment instanceof CommitStatment) { if (logger.isDebugEnabled()) { logger.debug("CommitStatment:[" + sql + "]"); } return null; } else if (statment instanceof RollbackStatment) { if (logger.isDebugEnabled()) { logger.debug("RollbackStatment:[" + sql + "]"); } return null; } else { throw new RuntimeException("error,unknown statement:[" + sql + "]"); } ObjectPool[] pools = new ObjectPool[poolNames.size()]; int i = 0; for (String name : poolNames) { pools[i++] = ProxyRuntimeContext.getInstance().getPoolMap().get(name); } if (pools == null || pools.length == 0) { if (dmlStatment != null) { pools = dmlStatment.isReadStatment() ? this.readPools : this.writePools; if (logger.isDebugEnabled() && pools != null && pools.length > 0) { if (dmlStatment.isReadStatment()) { logger.debug("[" + sql + "] route to queryRouter readPool:" + readPool + "\n"); } else { logger.debug("[" + sql + "] route to queryRouter writePool:" + writePool + "\n"); } } } if (pools == null || pools.length == 0) { pools = this.defaultPools; if (logger.isDebugEnabled() && pools != null && pools.length > 0) { logger.debug("[" + sql + "] route to queryRouter defaultPool:" + defaultPool + "\n"); } } } else { if (logger.isDebugEnabled() && pools != null && pools.length > 0) { logger.debug("[" + sql + "] route to pools:" + poolNames + "\n"); } } return pools; } /** * ���� PropertyStatment ����������ӵ����� * * @param conn ��ǰ��������� * @param statment ��ǰ�����Statment * @param parameters */ protected abstract void setProperty(DatabaseConnection conn, PropertyStatment statment, Object[] parameters); public void init() throws InitialisationException { defaultPools = new ObjectPool[] { ProxyRuntimeContext.getInstance().getPoolMap().get(defaultPool) }; if (defaultPools == null || defaultPools[0] == null) { throw new InitialisationException("default pool required!"); } if (readPool != null && !StringUtil.isEmpty(readPool)) { readPools = new ObjectPool[] { ProxyRuntimeContext.getInstance().getPoolMap().get(readPool) }; } if (writePool != null && !StringUtil.isEmpty(writePool)) { writePools = new ObjectPool[] { ProxyRuntimeContext.getInstance().getPoolMap().get(writePool) }; } map = new LRUMap(LRUMapSize); class ConfigCheckTread extends Thread { long lastRuleModified; long lastFunFileModified; long lastRuleFunctionFileModified; File ruleFile; File funFile; File ruleFunctionFile; private ConfigCheckTread(){ this.setDaemon(true); this.setName("ruleConfigCheckThread"); ruleFile = new File(AbstractQueryRouter.this.ruleConfig); funFile = new File(AbstractQueryRouter.this.functionConfig); lastRuleModified = ruleFile.lastModified(); lastFunFileModified = funFile.lastModified(); if (AbstractQueryRouter.this.ruleFunctionConfig != null) { ruleFunctionFile = new File(AbstractQueryRouter.this.ruleFunctionConfig); lastRuleFunctionFileModified = ruleFunctionFile.lastModified(); } } public void run() { while (true) { try { Thread.sleep(5000l); Map<String, Function> funMap = null; Map<String, PostfixCommand> ruleFunMap = null; Map<Table, TableRule> tableRuleMap = null; try { if (AbstractQueryRouter.this.functionConfig != null) { if (funFile.lastModified() != lastFunFileModified) { try { funMap = loadFunctionMap(AbstractQueryRouter.this.functionConfig); } catch (ConfigurationException exception) { } } } if (AbstractQueryRouter.this.ruleFunctionConfig != null) { if (ruleFunctionFile.lastModified() != lastRuleFunctionFileModified) { ruleFunMap = loadRuleFunctionMap(AbstractQueryRouter.this.ruleFunctionConfig); } } if (AbstractQueryRouter.this.ruleConfig != null) { if (ruleFile.lastModified() != lastRuleModified || (AbstractQueryRouter.this.ruleFunctionConfig != null && ruleFunctionFile.lastModified() != lastRuleFunctionFileModified)) { tableRuleMap = loadConfig(AbstractQueryRouter.this.ruleConfig); } } if (funMap != null) { AbstractQueryRouter.this.functionMap = funMap; } if (ruleFunMap != null) { AbstractQueryRouter.this.ruleFunctionMap = ruleFunMap; } if (tableRuleMap != null) { AbstractQueryRouter.this.tableRuleMap = tableRuleMap; } } catch (ConfigurationException e) { } finally { if (funFile != null && funFile.exists()) { lastFunFileModified = funFile.lastModified(); } if (ruleFunctionFile != null && ruleFunctionFile.exists()) { lastRuleFunctionFileModified = ruleFunctionFile.lastModified(); } if (ruleFile != null && ruleFile.exists()) { lastRuleModified = ruleFile.lastModified(); } } } catch (InterruptedException e) { } } } } if (needParse) { boolean configNeedCheck = false; if (AbstractQueryRouter.this.functionConfig != null) { this.functionMap = loadFunctionMap(AbstractQueryRouter.this.functionConfig); configNeedCheck = true; } else { needEvaluate = false; } if (AbstractQueryRouter.this.ruleFunctionConfig != null) { AbstractQueryRouter.this.ruleFunctionMap = loadRuleFunctionMap(AbstractQueryRouter.this.ruleFunctionConfig); configNeedCheck = true; } if (AbstractQueryRouter.this.ruleConfig != null) { this.tableRuleMap = loadConfig(this.ruleConfig); configNeedCheck = true; } else { needEvaluate = false; } if (configNeedCheck) { new ConfigCheckTread().start(); } } } public static Map<String, Function> loadFunctionMap(String configFileName) { FunctionLoader<String, Function> loader = new FunctionLoader<String, Function>() { @Override public void initBeanObject(BeanObjectEntityConfig config, Function bean) { bean.setName(config.getName()); } @Override public void putToMap(Map<String, Function> map, Function value) { map.put(value.getName(), value); } }; loader.setDTD("/com/meidusa/amoeba/xml/function.dtd"); loader.setDTDSystemID("function.dtd"); return loader.loadFunctionMap(configFileName); } public static Map<String, PostfixCommand> loadRuleFunctionMap(String configFileName) { FunctionLoader<String, PostfixCommand> loader = new FunctionLoader<String, PostfixCommand>() { @Override public void initBeanObject(BeanObjectEntityConfig config, PostfixCommand bean) { bean.setName(config.getName()); } @Override public void putToMap(Map<String, PostfixCommand> map, PostfixCommand value) { map.put(value.getName(), value); } }; loader.setDTD("/com/meidusa/amoeba/xml/function.dtd"); loader.setDTDSystemID("function.dtd"); Map<String, PostfixCommand> tempRuleFunMap = new HashMap<String, PostfixCommand>(); Map<String, PostfixCommand> defindMap = loader.loadFunctionMap(configFileName); tempRuleFunMap.putAll(ruleFunTab); tempRuleFunMap.putAll(defindMap); return tempRuleFunMap; } private Map<Table, TableRule> loadConfig(String configFileName) { DocumentBuilder db; 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("rule.dtd")) { InputStream in = AbstractQueryRouter.class.getResourceAsStream("/com/meidusa/amoeba/xml/rule.dtd"); if (in == null) { LogLog.error("Could not find [rule.dtd]. Used [" + AbstractQueryRouter.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; } }); 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); } } private Map<Table, TableRule> loadConfigurationFile(String fileName, DocumentBuilder db) throws InitialisationException { Document doc = null; InputStream is = null; Map<Table, TableRule> tableRuleMap = new HashMap<Table, TableRule>(); try { is = new FileInputStream(new 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("tableRule")) { TableRule rule = loadTableRule(child); tableRuleMap.put(rule.table.getName() == null ? null : rule.table, rule); } } } if (logger.isInfoEnabled()) { logger.info("Loaded rule configuration from: " + fileName); } return tableRuleMap; } private TableRule loadTableRule(Element current) throws InitialisationException { TableRule tableRule = new TableRule(); String name = current.getAttribute("name"); String schemaName = current.getAttribute("schema"); Table table = new Table(); table.setName(name); if (!StringUtil.isEmpty(schemaName)) { Schema schema = new Schema(); schema.setName(schemaName); table.setSchema(schema); } tableRule.table = table; String defaultPools = current.getAttribute("defaultPools"); if (defaultPools != null) { tableRule.defaultPools = readTokenizedString(defaultPools, " ,"); } String readPools = current.getAttribute("readPools"); if (readPools != null) { tableRule.readPools = readTokenizedString(readPools, " ,"); } String writePools = current.getAttribute("writePools"); if (writePools != null) { tableRule.writePools = readTokenizedString(writePools, " ,"); } 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; final String nodeName = child.getNodeName(); if (nodeName.equals("rule")) { tableRule.ruleList.add(loadRule(child, tableRule)); } } } return tableRule; } private Rule loadRule(Element current, TableRule tableRule) throws InitialisationException { Rule rule = new Rule(); // root rule.name = current.getAttribute("name"); String group = current.getAttribute("group"); rule.group = StringUtil.isEmpty(group) ? null : group; String ignoreArray = current.getAttribute("ignoreArray"); rule.ignoreArray = Boolean.parseBoolean(ignoreArray); String isSwitch = current.getAttribute("isSwitch"); rule.isSwitch = Boolean.parseBoolean(isSwitch); // parameters Element parametersNode = DocumentUtil.getTheOnlyElement(current, "parameters"); if (parametersNode != null) { String[] tokens = readTokenizedString(parametersNode.getTextContent(), " ,"); int index = 0; for (String parameter : tokens) { rule.parameterMap.put(parameter, index); Column column = new Column(); column.setName(parameter); column.setTable(tableRule.table); rule.cloumnMap.put(column, index); index++; } tokens = readTokenizedString(parametersNode.getAttribute("excludes"), " ,"); if (tokens != null) { for (String parameter : tokens) { Column column = new Column(); column.setName(parameter); column.setTable(tableRule.table); rule.excludes.add(column); } } } // expression Element expression = DocumentUtil.getTheOnlyElement(current, "expression"); rule.expression = expression.getTextContent(); rule.rowJep = new RowJEP(rule.expression); try { rule.rowJep.parseExpression(rule.parameterMap, (Map<String, Variable>) null, this.ruleFunctionMap); } catch (com.meidusa.amoeba.sqljep.ParseException e) { throw new InitialisationException("parser expression:" + rule.expression + " error", e); } // defaultPools Element defaultPoolsNode = DocumentUtil.getTheOnlyElement(current, "defaultPools"); if (defaultPoolsNode != null) { String defaultPools = defaultPoolsNode.getTextContent(); rule.defaultPools = readTokenizedString(defaultPools, " ,"); } // readPools Element readPoolsNode = DocumentUtil.getTheOnlyElement(current, "readPools"); if (readPoolsNode != null) { rule.readPools = readTokenizedString(readPoolsNode.getTextContent(), " ,"); } // writePools Element writePoolsNode = DocumentUtil.getTheOnlyElement(current, "writePools"); if (writePoolsNode != null) { rule.writePools = readTokenizedString(writePoolsNode.getTextContent(), " ,"); } return rule; } public static String[] readTokenizedString(String string, String delim) { return StringUtil.split(string, delim); } public int getLRUMapSize() { return LRUMapSize; } public String getDefaultPool() { return defaultPool; } public void setDefaultPool(String defaultPoolName) { this.defaultPool = defaultPoolName; } public void setLRUMapSize(int mapSize) { LRUMapSize = mapSize; } public String getFunctionConfig() { return functionConfig; } public void setFunctionConfig(String functionConfig) { this.functionConfig = functionConfig; } public boolean isNeedParse() { return needParse; } public void setNeedParse(boolean needParse) { this.needParse = needParse; } public void setRuleFunctionConfig(String ruleFunctionConfig) { this.ruleFunctionConfig = ruleFunctionConfig; } public ObjectPool getObjectPool(Object key) { if (key instanceof String) { return ProxyRuntimeContext.getInstance().getPoolMap().get(key); } else { for (ObjectPool pool : ProxyRuntimeContext.getInstance().getPoolMap().values()) { if (pool.hashCode() == key.hashCode()) { return pool; } } } return null; } public Statment parseSql(DatabaseConnection connection, String sql) { Statment statment = null; String defaultSchema = (connection == null || StringUtil.isEmpty(connection.getSchema())) ? null : connection.getSchema(); long sqlKey = ((long) sql.length() << 32) | (long) (defaultSchema != null ? (defaultSchema.hashCode() ^ sql.hashCode()) : sql.hashCode()); mapLock.lock(); try { statment = (Statment) map.get(sqlKey); } finally { mapLock.unlock(); } if (statment == null) { synchronized (sql) { statment = (Statment) map.get(sqlKey); if (statment != null) { return statment; } Parser parser = newParser(sql); parser.setFunctionMap(this.functionMap); if (defaultSchema != null) { Schema schema = new Schema(); schema.setName(defaultSchema); parser.setDefaultSchema(schema); } try { try { statment = parser.doParse(); mapLock.lock(); try { map.put(sqlKey, statment); } finally { mapLock.unlock(); } } catch (Error e) { logger.error(sql, e); return null; } } catch (ParseException e) { logger.error(sql, e); return null; } } } return statment; } public int parseParameterCount(DatabaseConnection connection, String sql) { Statment statment = parseSql(connection, sql); return statment.getParameterCount(); } }