/** * Alipay.com Inc. * Copyright (c) 2004-2012 All Rights Reserved. */ package com.alipay.zdal.client.jdbc.dbselector; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import javax.sql.DataSource; import org.apache.log4j.Logger; import com.alipay.zdal.client.ThreadLocalString; import com.alipay.zdal.client.jdbc.ZdalStatement.DB_OPERATION_TYPE; import com.alipay.zdal.client.util.ThreadLocalMap; import com.alipay.zdal.common.Constants; import com.alipay.zdal.common.OperationDBType; import com.alipay.zdal.common.RuntimeConfigHolder; import com.alipay.zdal.common.WeightRandom; import com.alipay.zdal.common.lang.StringUtil; /** * �Ե����ݿ������ * �����Ƕ��Եȣ��������⣬ÿ�����������ȫ��ͬ���Եȶ�ȡ * ������д�Եȣ�����־�⣬ÿ�������ݲ�ͬ��һ������д���ĸ��ⶼ���ԡ��Ե�д�� * * ֧�ֶ�̬����Ȩ�أ���̬�Ӽ��� * * * @param <T> JdbcTemplate ���� DataSource * * TODO ��DataSource�������ж��Dz���ͬһ���⣬���ٱ�������������Ϣ��Map<String, Map<String, Object>> */ public class EquityDbManager extends AbstractDBSelector { private static final Logger logger = Logger.getLogger(EquityDbManager.class); /** * �����ڻᶯ̬�ı��״̬�����ֲ������ֻ���ؽ��������޸ġ� */ private static class DbRuntime { public final Map<String, DataSource> dataSources; //���ս�� public final Map<String, DataSourceHolder> dataSourceHolders; //��װ����datasource����,��Ҫ�����Ƿ�ɶ������� public final WeightRandom weightRandom; public DbRuntime(Map<String, DataSource> dataSources, WeightRandom weightRandom) { this.dataSources = Collections.unmodifiableMap(dataSources); this.weightRandom = weightRandom; this.dataSourceHolders = getDataSourceHolders(dataSources); } /** * �õ���װ����datasource���ϣ� * @param dataSources * @return */ public Map<String, DataSourceHolder> getDataSourceHolders( Map<String, DataSource> dataSources) { Map<String, DataSourceHolder> map = new HashMap<String, DataSourceHolder>(dataSources .size()); for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) { map.put(entry.getKey(), new DataSourceHolder(entry.getValue())); } return Collections.unmodifiableMap(map); } public DataSource select() { return this.dataSources.get(this.weightRandom.select(null)); } } private final RuntimeConfigHolder<DbRuntime> dbHolder = new RuntimeConfigHolder<DbRuntime>(); /** * key:��־���ֵ����������Ȩ��ʱʹ�� * ��־������properties�ļ����Զ����key, �ڷֿ�ֱ����������dbSelectId_index * value����־���Ӧ��JdbcTemplate */ private Map<String, DataSource> initDataSources; //���ս�� // /** // * key: һ��log����Դ��ֵ // * value:����Դ����Properties��Ӧ��Map // * key������Դ��Ҫ�IJ�����driver,username.. // * value:������ֵ // */ // private Map<String, DataSourceConfig> initDataSourceConfigs; private String dataSourceConfigFile; public EquityDbManager(String id) { super(id); } public EquityDbManager(String id, Map<String, DataSource> initDataSources) { super(id); this.initDataSources = initDataSources; try { this.init(); } catch (IOException e) { logger.error("Should not happen!!", e); //��Ϊ���ڽ���dataSourceConfigFile�ŻᱨIOException } } public EquityDbManager(String id, Map<String, DataSource> initDataSources, Map<String, Integer> weights) { this(id, initDataSources); if (weights != null) { setWeightRandom(new WeightRandom(weights)); } } /** * �����������ȼ���1.dataSources 2.dataSourceConfigs 3.dataSourceConfigFile 4.���� */ public void init() throws IOException { //��ʼ������Դ if (this.initDataSources != null) { //���ֱ��������syncLogDataSources����ֱ��ʹ�ã���֧�ֶ��ĺͶ�̬�޸� WeightRandom weightRandom = new WeightRandom(this.initDataSources.keySet().toArray( new String[0])); this.dbHolder.set(new DbRuntime(this.initDataSources, weightRandom)); } // else if (this.initDataSourceConfigs != null) { // //���ֱ��������syncLogDataSourceConfigs����ֱ��ʹ�ã���֧�ֶ��ĺͶ�̬�޸� // //initDataSources(this.initDataSourceConfigs); // } else if (this.dataSourceConfigFile != null) { //���ֱ��������dataSourceConfigFile��������ļ�����֧�ֶ��ĺͶ�̬�޸� Properties p = new Properties(); if (dataSourceConfigFile.startsWith("/")) { dataSourceConfigFile = StringUtil.substringAfter(dataSourceConfigFile, "/"); } InputStream inputStream = getClass().getClassLoader().getResourceAsStream( dataSourceConfigFile); if (null == inputStream) { throw new IllegalArgumentException("dataSource�����ļ�������: " + dataSourceConfigFile); } p.load(inputStream); //initDataSources(parseDataSourceConfig(p)); } else { } } /** * ����Ȩ�أ��������һ��DataSource * @return */ public DataSource select() { return this.dbHolder.get().select(); } //TODO ���ǽӿ��Ƿ���СΪֻ����DataSource[] public Map<String, DataSource> getDataSources() { return this.dbHolder.get().dataSources; } public Map<String, Integer> getWeights() { return this.dbHolder.get().weightRandom.getWeightConfig(); } /** * ������������ݿ�������ִ��һ���ص�������ʧ���˸���Ȩ��ѡ��һ�������� * �Ը���Ȩ��ѡ�񵽵�DataSource�����û���������ò���args�����Ե���DataSourceTryer��tryOnDataSource���� * @param failedDataSources ��֪��ʧ��DS�����쳣 * @param args ͸����DataSourceTryer��tryOnDataSource������ * @return null��ʾִ�гɹ��������ʾ���Դ���ִ��ʧ�ܣ�����SQLException�б� */ public <T> T tryExecute(Map<DataSource, SQLException> failedDataSources, DataSourceTryer<T> tryer, int times, DB_OPERATION_TYPE operationType, Object... args) throws SQLException { List<SQLException> exceptions = new ArrayList<SQLException>(0); List<String> excludeKeys = new ArrayList<String>(0); DbRuntime dbrt = this.dbHolder.get(); WeightRandom wr = dbrt.weightRandom; if (failedDataSources != null) { exceptions.addAll(failedDataSources.values()); times = times - failedDataSources.size(); //�۳��Ѿ�ʧ�ܵ������Դ��� for (SQLException e : failedDataSources.values()) { if (!exceptionSorter.isExceptionFatal(e)) { //��һ���쳣����ʵ����������쳣����map�޷�֪��˳��ֻ�ܱ������������ݿⲻ�����쳣�����׳� //�Dz���Ӧ���ڷ��ַ����ݿ�fatal֮��������׳��������Ƿŵ�failedDataSources���map��?(guangxia) return tryer.onSQLException(exceptions, exceptionSorter, args); } } } String name = null; //���ָ����ij������ Integer dbIndex = (Integer) ThreadLocalMap.get(ThreadLocalString.DATABASE_INDEX); for (int i = 0; i < times; i++) { if (i == 0 && dbIndex != null) { //�����ָ���˿�� name = this.getId() + Constants.DBINDEX_DSKEY_CONN_CHAR + dbIndex; } else { //���ѡ�� name = wr.select(excludeKeys); } if (name == null) { exceptions.add(new NoMoreDataSourceException("��ִ��sql�Ĺ����У�û�п�������Դ�ˣ�")); logger.warn("�ڴ˴�ִ��sql�Ĺ����У�����Դ" + wr.getAllDbKeys() + "���������ˣ�"); break; } //��ȡ��db�����֣�Ȼ�󻺴�������ҵ����õ������� Map<String, DataSource> map = new HashMap<String, DataSource>(); //����ǰ׺��by���� 20130903 map.put(getAppDsName() + "." + name, null); ThreadLocalMap.put(ThreadLocalString.GET_ID_AND_DATABASE, map); DataSourceHolder selectedDS = dbrt.dataSourceHolders.get(name); if (selectedDS == null) { //��Ӧ�ó��ֵġ���ʼ���߼�Ӧ�ñ�֤�յ�����Դ(null)���ᱻ����DataSourceHolders throw new IllegalStateException("Can't find DataSource for name:" + name + "from dataSourceHolders!"); } if (failedDataSources != null && failedDataSources.containsKey(selectedDS.getDs())) { excludeKeys.add(name); if (dbIndex == null) { i--; } continue; } if (logger.isDebugEnabled()) { logger.debug("selected database name=" + name); } int size1 = excludeKeys.size(); //���������Դ�Ѿ�����ʶΪ������ T t = selectedDS.tryOnSelectedDataSource(operationType, wr, dbrt.dataSourceHolders, failedDataSources, tryer, exceptions, excludeKeys, exceptionSorter, name, args); boolean isAddedIntoExcludeKeys = excludeKeys.size() - size1 > 0; if (isAddedIntoExcludeKeys) { continue; } else { return t; } } //return exceptions; //�����Է���ҵ���log, ����tryExecute�϶���������ȥ��throwSQLException return tryer.onSQLException(exceptions, exceptionSorter, args); } public static interface DataSourceChangeListener { public void onDataSourceChanged(Map<String, DataSource> dataSources); } public void setWeight(Map<String, Integer> weightMap) { setWeightRandom(new WeightRandom(weightMap)); } /** * ֧�ֶ�̬�޸�Ȩ�� */ synchronized boolean setWeightRandom(WeightRandom weightRandom) { if (weightRandom == null) { return false; } Map<String, Integer> newWeight = weightRandom.getWeightConfig(); DbRuntime dbrt = EquityDbManager.this.dbHolder.get(); for (String newkey : newWeight.keySet()) { if (!dbrt.dataSources.containsKey(newkey)) { logger.error("��Ȩ�ص�����Դ��������������Դ�в�����:" + newkey); return false; } } if (newWeight.size() < dbrt.dataSources.size()) { logger.warn("��Ȩ�ص�����Դ���Ƹ���С��ԭ������Դ��"); return false; //������������������ȫһЩ } Map<String, DataSource> dataSources = new HashMap<String, DataSource>(dbrt.dataSources .size()); dataSources.putAll(dbrt.dataSources); DbRuntime newrt = new DbRuntime(dataSources, weightRandom); EquityDbManager.this.dbHolder.set(newrt); return true; } /* * �Եȿ�Ĭ�ϲ������ԣ�ֻ�ж���Ž��ж����ԣ�д�ⲻ����д���ԣ� */ public boolean isSupportRetry(OperationDBType type) { boolean flag = false; String dbSelectorId = getId(); if (dbSelectorId.endsWith("_r") || (type == OperationDBType.readFromDb)) { flag = true; } return flag; } /** * ���߼���getter/setter */ public void setInitDataSources(Map<String, DataSource> initDataSources) { this.initDataSources = initDataSources; } public void setDataSourceConfigFile(String dataSourceConfigFile) { this.dataSourceConfigFile = dataSourceConfigFile; } }