Java tutorial
/** * <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.route; import java.io.File; 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 java.util.regex.Pattern; import org.apache.commons.collections.map.LRUMap; import org.apache.log4j.Logger; import com.meidusa.amoeba.config.BeanObjectEntityConfig; import com.meidusa.amoeba.config.ConfigurationException; import com.meidusa.amoeba.context.ContextChangedListener; import com.meidusa.amoeba.context.ProxyRuntimeContext; import com.meidusa.amoeba.net.Connection; import com.meidusa.amoeba.net.poolable.ObjectPool; import com.meidusa.amoeba.parser.ParseException; import com.meidusa.amoeba.parser.dbobject.Column; import com.meidusa.amoeba.parser.dbobject.Table; import com.meidusa.amoeba.parser.function.Function; import com.meidusa.amoeba.parser.statement.Statement; import com.meidusa.amoeba.sqljep.function.Comparative; import com.meidusa.amoeba.sqljep.function.ComparativeBaseList; import com.meidusa.amoeba.util.Initialisable; import com.meidusa.amoeba.util.InitialisationException; import com.meidusa.amoeba.util.StringUtil; import com.meidusa.amoeba.util.ThreadLocalMap; import com.meidusa.amoeba.util.Tuple; /** * @author struct */ public abstract class AbstractQueryRouter<T extends Connection, V> implements QueryRouter<T, V>, Initialisable, ContextChangedListener { public static final String _CURRENT_QUERY_OBJECT_ = "_CURRENT_QUERY_OBJECT_"; protected static Logger logger = Logger.getLogger(AbstractQueryRouter.class); private Map<String, Pattern> patternMap = new HashMap<String, Pattern>(); /* 1000 */ private int LRUMapSize = 1000; protected LRUMap map; protected Lock mapLock = new ReentrantLock(false); private Map<Table, TableRule> tableRuleMap = new HashMap<Table, TableRule>(); private Map<Table, TableRule> regexTableRuleMap = new HashMap<Table, TableRule>(); protected Map<String, Function> functionMap = new HashMap<String, Function>(); protected ObjectPool[] defaultPools; protected ObjectPool[] readPools; protected ObjectPool[] writePools; protected Tuple<Statement, ObjectPool[]> tuple; private File sqlFunctionFile; private String defaultPool; private String readPool; private String writePool; private boolean needParse = true; private TableRuleLoader ruleLoader; public TableRuleLoader getRuleLoader() { return ruleLoader; } public void setRuleLoader(TableRuleLoader ruleLoader) { this.ruleLoader = ruleLoader; } public AbstractQueryRouter() { } 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(T connection, V queryObject) throws ParseException { if (queryObject == null) { return defaultPools; } if (needParse) { return selectPool(connection, queryObject); } else { return defaultPools; } } protected abstract Map<Table, Map<Column, Comparative>> evaluateTable(T connection, V queryObject); /** * Query route ObjectPool nullDatabaseConnection * @throws ParseException */ protected void beforeSelectPool(T connection, V queryObject) { ThreadLocalMap.put(_CURRENT_QUERY_OBJECT_, queryObject); } protected List<String> evaluate(StringBuffer loggerBuffer, T connection, V queryObject) { boolean isRead = true; boolean isPrepared = false; if (queryObject instanceof Request) { isRead = ((Request) queryObject).isRead(); isPrepared = ((Request) queryObject).isPrepared(); } List<String> poolNames = new ArrayList<String>(); Map<Table, Map<Column, Comparative>> tables = evaluateTable(connection, queryObject); 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) { boolean regexMatched = false; Map<Column, Comparative> columnMap = entry.getValue(); TableRule tableRule = this.tableRuleMap.get(entry.getKey()); Table table = entry.getKey(); if (tableRule == null && table.getName() != null) { /** * foreach regex table rule */ for (Map.Entry<Table, TableRule> ruleEntry : this.regexTableRuleMap.entrySet()) { Table ruleTable = ruleEntry.getKey(); boolean tableMatched = false; boolean schemaMatched = false; /** * check table name matched or not. */ Pattern pattern = this.getPattern(ruleTable.getName()); java.util.regex.Matcher matcher = pattern.matcher(table.getName()); if (matcher.find()) { tableMatched = true; } /** * check table schema matched or not. */ pattern = this.getPattern(ruleTable.getSchema().getName()); matcher = pattern.matcher(table.getSchema().getName()); if (matcher.find()) { schemaMatched = true; } if (tableMatched && schemaMatched) { tableRule = ruleEntry.getValue(); regexMatched = true; break; } } } // table Rule Rule if (tableRule != null) { // sqltableRule if (columnMap == null || isPrepared) { String[] pools = (isRead ? 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 (!isPrepared) { if (logger.isDebugEnabled()) { loggerBuffer.append(", 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; } } // if (columnMap.size() < rule.parameterMap.size()) { continue; } else { boolean matched = true; // 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()]; // dmlstatement for (Map.Entry<Column, Integer> parameter : rule.cloumnMap.entrySet()) { Comparative condition = null; if (regexMatched) { Column column = new Column(); column.setName(parameter.getKey().getName()); column.setTable(table); condition = columnMap.get(column); } else { 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.result == RuleResult.INDEX) { i = (Integer) ((Comparative) result).getValue(); if (i < 0) { continue; } matched = true; } else if (rule.result == RuleResult.POOLNAME) { String matchedPoolsString = ((Comparative) result).getValue().toString(); String[] poolNamesMatched = matchedPoolsString.split(","); if (poolNamesMatched != null && poolNamesMatched.length > 0) { for (String poolName : poolNamesMatched) { if (!poolNames.contains(poolName)) { poolNames.add(poolName); } } if (logger.isDebugEnabled()) { loggerBuffer.append(", matched table:" + tableRule.table + ", rule:" + rule.name); } } continue; } else { matched = (Boolean) ((Comparative) result).getValue(); } } else { if (rule.result == RuleResult.INDEX) { i = (Integer) Integer.valueOf(result.toString()); if (i < 0) { continue; } matched = true; } else if (rule.result == RuleResult.POOLNAME) { String matchedPoolsString = result.toString(); String[] poolNamesMatched = StringUtil.split(matchedPoolsString, ";,"); if (poolNamesMatched != null && poolNamesMatched.length > 0) { for (String poolName : poolNamesMatched) { if (!poolNames.contains(poolName)) { poolNames.add(poolName); } } if (logger.isDebugEnabled()) { loggerBuffer.append(", matched table:" + tableRule.table + ", rule:" + rule.name); } } continue; } else { matched = (Boolean) result; } } if (matched) { if (rule.group != null) { groupMatched.add(rule.group); } String[] pools = (isRead ? 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()) { loggerBuffer.append( ", matched table:" + tableRule.table + ", rule:" + rule.name); } } } catch (com.meidusa.amoeba.sqljep.ParseException e) { // logger.error("parse rule error:"+rule.expression,e); } } } // TableRulepool if (poolNames.size() == 0) { String[] pools = (isRead ? tableRule.readPools : tableRule.writePools); if (pools == null || pools.length == 0) { pools = tableRule.defaultPools; } if (!isPrepared) { if (tableRule.ruleList != null && tableRule.ruleList.size() > 0) { if (logger.isDebugEnabled()) { loggerBuffer.append(", no rule matched, using tableRule:[" + tableRule.table + "] defaultPools"); } } else { if (logger.isDebugEnabled()) { if (pools != null) { StringBuffer buffer = new StringBuffer(); for (String pool : pools) { buffer.append(pool).append(","); } loggerBuffer.append(", using tableRule:[" + tableRule.table + "] defaultPools=" + buffer.toString()); } } } } for (String poolName : pools) { if (!poolNames.contains(poolName)) { poolNames.add(poolName); } } } } } } return poolNames; } public ObjectPool[] selectPool(T connection, V queryObject) { beforeSelectPool(connection, queryObject); StringBuffer loggerBuffer = null; boolean isRead = true; if (queryObject instanceof Request) { isRead = ((Request) queryObject).isRead(); } if (logger.isDebugEnabled()) { loggerBuffer = new StringBuffer("query="); loggerBuffer.append(queryObject); if (queryObject instanceof Request) { loggerBuffer.append(((Request) queryObject).isPrepared() ? ",prepared=true" : ""); } } List<String> poolNames = new ArrayList<String>(); poolNames = evaluate(loggerBuffer, connection, queryObject); ObjectPool[] pools = new ObjectPool[poolNames.size()]; int i = 0; for (String name : poolNames) { ObjectPool pool = ProxyRuntimeContext.getInstance().getPoolMap().get(name); if (pool == null) { logger.error("cannot found Pool=" + name + ",sqlObject=" + queryObject); throw new RuntimeException("cannot found Pool=" + name + ",sqlObject=" + queryObject); } pools[i++] = pool; } if (pools == null || pools.length == 0) { pools = (isRead ? this.readPools : this.writePools); if (logger.isDebugEnabled() && pools != null && pools.length > 0) { if (isRead) { loggerBuffer.append(", route to queryRouter readPool:" + readPool + "\r\n"); } else { loggerBuffer.append(", route to queryRouter writePool:" + writePool + "\r\n"); } } if (pools == null || pools.length == 0) { pools = this.defaultPools; if (logger.isDebugEnabled() && pools != null && pools.length > 0) { loggerBuffer.append(", route to queryRouter defaultPool:" + defaultPool + "\r\n"); } } } else { if (logger.isDebugEnabled() && pools != null && pools.length > 0) { loggerBuffer.append(", route to pools:" + poolNames + "\r\n"); } } if (logger.isDebugEnabled()) { if (loggerBuffer != null) { logger.debug(loggerBuffer.toString()); } } return pools; } public void doChange() { defaultPools = new ObjectPool[] { ProxyRuntimeContext.getInstance().getPoolMap().get(defaultPool) }; 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) }; } } 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!,defaultPool=" + defaultPool + " invalid"); } if (readPool != null && !StringUtil.isEmpty(readPool)) { ObjectPool pool = ProxyRuntimeContext.getInstance().getPoolMap().get(readPool); if (pool == null) { logger.error("cannot found Pool=" + readPool); throw new InitialisationException("cannot found Pool=" + readPool); } readPools = new ObjectPool[] { pool }; } if (writePool != null && !StringUtil.isEmpty(writePool)) { ObjectPool pool = ProxyRuntimeContext.getInstance().getPoolMap().get(writePool); if (pool == null) { logger.error("cannot found Pool=" + writePool); throw new InitialisationException("cannot found Pool=" + writePool); } writePools = new ObjectPool[] { pool }; } map = new LRUMap(LRUMapSize); class ConfigCheckTread extends Thread { long lastFunFileModified; private ConfigCheckTread() { this.setDaemon(true); this.setName("ruleConfigCheckThread"); if (sqlFunctionFile != null) { lastFunFileModified = sqlFunctionFile.lastModified(); } } public void run() { while (true) { try { Thread.sleep(5000l); Map<String, Function> funMap = null; Map<Table, TableRule> tableRuleMap = null; try { if (AbstractQueryRouter.this.sqlFunctionFile != null) { if (sqlFunctionFile.lastModified() != lastFunFileModified) { try { funMap = loadFunctionMap( AbstractQueryRouter.this.sqlFunctionFile.getAbsolutePath()); logger.info("loading FunctionMap from File=" + sqlFunctionFile); } catch (ConfigurationException exception) { } } } if (ruleLoader.needLoad()) { tableRuleMap = ruleLoader.loadRule(); if (tableRuleMap != null) { for (Map.Entry<Table, TableRule> ruleEntry : tableRuleMap.entrySet()) { Table ruleTable = ruleEntry.getKey(); if (ruleTable.getName().indexOf("*") >= 0 || (ruleTable.getSchema().getName() != null && ruleTable.getSchema().getName().indexOf("*") >= 0) || ruleTable.getName().indexOf("^") >= 0 || (ruleTable.getSchema().getName() != null && ruleTable.getSchema().getName().indexOf("^") >= 0)) { getPattern(ruleTable.getName()); regexTableRuleMap.put(ruleTable, ruleEntry.getValue()); } } } } if (funMap != null) { AbstractQueryRouter.this.functionMap = funMap; } if (tableRuleMap != null) { AbstractQueryRouter.this.tableRuleMap = tableRuleMap; } } catch (ConfigurationException e) { } finally { if (sqlFunctionFile != null && sqlFunctionFile.exists()) { lastFunFileModified = sqlFunctionFile.lastModified(); } } } catch (InterruptedException e) { } } } } if (needParse) { if (AbstractQueryRouter.this.sqlFunctionFile != null) { this.functionMap = loadFunctionMap(AbstractQueryRouter.this.sqlFunctionFile.getAbsolutePath()); } this.tableRuleMap = ruleLoader.loadRule(); if (tableRuleMap != null) { for (Map.Entry<Table, TableRule> ruleEntry : this.tableRuleMap.entrySet()) { Table ruleTable = ruleEntry.getKey(); if (ruleTable.getName().indexOf("*") >= 0 || (ruleTable.getSchema().getName() != null && ruleTable.getSchema().getName().indexOf("*") >= 0) || ruleTable.getName().indexOf("^") >= 0 || (ruleTable.getSchema().getName() != null && ruleTable.getSchema().getName().indexOf("^") >= 0)) { this.getPattern(ruleTable.getName()); regexTableRuleMap.put(ruleTable, ruleEntry.getValue()); } } } 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"); logger.info("loading FunctionMap from File=" + configFileName); return loader.loadFunctionMap(configFileName); } 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 File getSqlFunctionFile() { return sqlFunctionFile; } public void setSqlFunctionFile(File sqlFunctionFile) { this.sqlFunctionFile = sqlFunctionFile; } public boolean isNeedParse() { return needParse; } public void setNeedParse(boolean needParse) { this.needParse = needParse; } 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 ObjectPool[] getDefaultObjectPool() { return this.defaultPools; } private Pattern getPattern(String source) { if (source != null && source.indexOf("*") == 0) { source = "^" + source; } Pattern pattern = this.patternMap.get(source); if (pattern == null) { synchronized (patternMap) { pattern = this.patternMap.get(source); if (pattern == null) { pattern = Pattern.compile(source); } patternMap.put(source, pattern); } } return pattern; } public static void main(String[] aa) { String[] aaa = StringUtil.split("asdfasdf,asdf;aqwer", ";,"); for (String aaaaa : aaa) { System.out.println(aaaaa); } System.out.println(System.currentTimeMillis()); String source = "^fileSys_[a-zA-Z0-9_]*"; Pattern pattern = null; if (pattern == null) { pattern = Pattern.compile(source); } java.util.regex.Matcher matcher = pattern.matcher("fileSys_abc12d"); System.out.println(matcher.matches()); } }