Package com.taobao.tddl.jdbc.group.dbselector

Source Code of com.taobao.tddl.jdbc.group.dbselector.AbstractDBSelector

/*(C) 2007-2012 Alibaba Group Holding Limited. 
*This program is free software; you can redistribute it and/or modify 
*it under the terms of the GNU General Public License version 2 as 
* published by the Free Software Foundation. 
* Authors: 
*   junyu <junyu@taobao.com> , shenxun <shenxun@taobao.com>, 
*   linxuan <linxuan@taobao.com> ,qihao <qihao@taobao.com>  
*/ 
package com.taobao.tddl.jdbc.group.dbselector;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.taobao.tddl.client.jdbc.sorter.ExceptionSorter;
import com.taobao.tddl.client.jdbc.sorter.MySQLExceptionSorter;
import com.taobao.tddl.client.jdbc.sorter.OracleExceptionSorter;
import com.taobao.tddl.common.SQLPreParser;
import com.taobao.tddl.common.util.NagiosUtils;
import com.taobao.tddl.common.util.TStringUtil;
import com.taobao.tddl.interact.rule.bean.DBType;
import com.taobao.tddl.jdbc.group.DataSourceWrapper;
import com.taobao.tddl.jdbc.group.config.GroupExtraConfig;
import com.taobao.tddl.jdbc.group.exception.SqlForbidException;
import com.taobao.tddl.jdbc.group.util.StringUtils;

/**
* @author linxuan
* @author yangzhu
*
*/
public abstract class AbstractDBSelector implements DBSelector {

  private static final Log logger = LogFactory
      .getLog(AbstractDBSelector.class);
  private static final Map<DBType, ExceptionSorter> exceptionSorters = new HashMap<DBType, ExceptionSorter>(
      2);
  static {
    exceptionSorters.put(DBType.ORACLE, new OracleExceptionSorter());
    exceptionSorters.put(DBType.MYSQL, new MySQLExceptionSorter());
  }
  private DBType dbType = DBType.MYSQL;
  protected ExceptionSorter exceptionSorter = exceptionSorters.get(dbType);
  private String id = "undefined"; // idֵδʹ��

  private static final int default_retryBadDbInterval = 2000; // milliseconds
  protected static int retryBadDbInterval; // milliseconds
  static {
    int interval = default_retryBadDbInterval;
    String propvalue = System
        .getProperty("com.taobao.tddl.DBSelector.retryBadDbInterval");
    if (propvalue != null) {
      try {
        interval = Integer.valueOf(propvalue.trim());
      } catch (Exception e) {
        logger.error("", e);
      }
    }
    retryBadDbInterval = interval;
  }

  protected boolean readable = false;

  public void setReadable(boolean readable) {
    this.readable = readable;
  }

  protected boolean isSupportRetry = true; // Ĭ�������֧������

  public boolean isSupportRetry() {
    return isSupportRetry;
  }

  public void setSupportRetry(boolean isSupportRetry) {
    this.isSupportRetry = isSupportRetry;
  }

  public AbstractDBSelector() {
  }

  public AbstractDBSelector(String id) {
    this.id = id;
  }

  protected static class DataSourceHolder {
    public final DataSourceWrapper dsw;
    public final ReentrantLock lock = new ReentrantLock();
    public volatile boolean isNotAvailable = false;
    public volatile long lastRetryTime = 0;

    public DataSourceHolder(DataSourceWrapper dsw) {
      this.dsw = dsw;
    }
  }

  protected <T> T tryOnDataSourceHolder(DataSourceHolder dsHolder,
      Map<DataSource, SQLException> failedDataSources,
      DataSourceTryer<T> tryer, int times, Object... args)
      throws SQLException {
    List<SQLException> exceptions = new LinkedList<SQLException>();
    if (failedDataSources != null) {
      exceptions.addAll(failedDataSources.values());
    }
    if (failedDataSources != null
        && failedDataSources.containsKey(dsHolder.dsw)) {
      return tryer.onSQLException(exceptions, exceptionSorter, args);
    }

    try {
      if (dsHolder.isNotAvailable) {
        boolean toTry = System.currentTimeMillis()
            - dsHolder.lastRetryTime > retryBadDbInterval;
        if (toTry && dsHolder.lock.tryLock()) {
          try {
            T t = tryer.tryOnDataSource(dsHolder.dsw, args); // ͬһ��ʱ��ֻ����һ���̼߳���ʹ���������Դ��
            dsHolder.isNotAvailable = false; // ��һ���߳����ԣ�ִ�гɹ�����Ϊ���ã��Զ��ָ�
            return t;
          } finally {
            dsHolder.lastRetryTime = System.currentTimeMillis();
            dsHolder.lock.unlock();
          }
        } else {
          exceptions.add(new NoMoreDataSourceException("dsKey:"
              + dsHolder.dsw.getDataSourceKey()
              + " not Available,toTry:" + toTry));
          return tryer.onSQLException(exceptions, exceptionSorter,
              args);
        }
      } else {
        return tryer.tryOnDataSource(dsHolder.dsw, args); // ��һ�γɹ�ֱ�ӷ���
      }
    } catch (SQLException e) {
      if (exceptionSorter.isExceptionFatal(e)) {
        NagiosUtils.addNagiosLog(NagiosUtils.KEY_DB_NOT_AVAILABLE + "|"
            + dsHolder.dsw.getDataSourceKey(), e.getMessage());
        dsHolder.isNotAvailable = true;
      }
      exceptions.add(e);
      return tryer.onSQLException(exceptions, exceptionSorter, args);
    }
  }

  protected GroupExtraConfig groupExtraConfig ;

  public <T> T tryExecute(Map<DataSource, SQLException> failedDataSources,
      DataSourceTryer<T> tryer, int times, Object... args)
      throws SQLException {
    // dataSourceIndex����args���һ��.�Ժ�Ķ�Ҫע��
    // local set dataSourceIndex was placed first
    Integer dataSourceIndex = null;
    if (args != null && args.length > 0) {
      dataSourceIndex = (Integer) args[args.length - 1];
    }


    if (groupExtraConfig != null) {
      Boolean defaultMain = groupExtraConfig.isDefaultMain();
      Map<String, Integer> tableDsIndexMap = groupExtraConfig
          .getTableDsIndexMap();
      Map<String, Integer> sqlDsIndexMap = groupExtraConfig
          .getSqlDsIndexMap();
      Set<String> sqlForbidSet = groupExtraConfig.getSqlForbidSet();

      // 1.when batch ,args have no sql parameter,so,should check
      // the args
      // 2.table dataSourceIndex relation have 2th priority
      // 3.sql dataSourceIndex relation have 3th priority
      if (args != null && args.length > 0 && args[0] instanceof String) {
        if (sqlForbidSet != null && sqlForbidSet.size() > 0) {
          String sql = (String) args[0];
          String nomalSql = TStringUtil.fillTabWithSpace(sql);
          boolean isForbidden = false;
          if (sqlForbidSet.contains(nomalSql)) {
            isForbidden = true;
          }
          if (!isForbidden) {
            String actualTable = SQLPreParser
                .findTableName(nomalSql);
            for (String configSql : sqlForbidSet) {
              String nomalConfigSql = TStringUtil
                  .fillTabWithSpace(configSql);
              String actualConfigTable = SQLPreParser
                  .findTableName(nomalConfigSql);
              if (StringUtils.isTableFatherAndSon(
                  actualConfigTable, actualTable)) {
                nomalConfigSql = nomalConfigSql.replaceAll(
                    actualConfigTable, actualTable);
              }
              if (nomalConfigSql.equals(nomalSql)) {
                isForbidden = true;
                break;
              }
            }
          }
          if (isForbidden) {
            String message = "sql : '" + sql
                + "' is in forbidden set.";
            logger.error(message);
            throw new SqlForbidException(message);
          }
        }

        if (tableDsIndexMap != null
            && tableDsIndexMap.size() > 0
            && (dataSourceIndex == null || dataSourceIndex == NOT_EXIST_USER_SPECIFIED_INDEX)) {
          String sql = (String) args[0];
          String actualTable = SQLPreParser.findTableName(sql);
          dataSourceIndex = tableDsIndexMap.get(actualTable);
          if (dataSourceIndex == null
              || dataSourceIndex == NOT_EXIST_USER_SPECIFIED_INDEX) {
            Set<String> tableSet = tableDsIndexMap.keySet();
            for (String configTable : tableSet) {
              if (StringUtils.isTableFatherAndSon(configTable,
                  actualTable)) {
                dataSourceIndex = tableDsIndexMap
                    .get(configTable);
                break;
              }
            }
          }
        }

        if (sqlDsIndexMap != null
            && sqlDsIndexMap.size() > 0
            && (dataSourceIndex == null || dataSourceIndex == NOT_EXIST_USER_SPECIFIED_INDEX)) {
          String sql = ((String) args[0]).toLowerCase();
          String nomalSql = TStringUtil.fillTabWithSpace(sql);
          dataSourceIndex = sqlDsIndexMap.get(nomalSql);
          if (dataSourceIndex == null
              || dataSourceIndex == NOT_EXIST_USER_SPECIFIED_INDEX) {
            String actualTable = SQLPreParser
                .findTableName(nomalSql);
            Set<String> sqlSet = sqlDsIndexMap.keySet();
            for (String configSql : sqlSet) {
              String nomalConfigSql = TStringUtil
                  .fillTabWithSpace(configSql);
              String actualConfigTable = SQLPreParser
                  .findTableName(nomalConfigSql);
              if (StringUtils.isTableFatherAndSon(
                  actualConfigTable, actualTable)) {
                nomalConfigSql = nomalConfigSql.replaceAll(
                    actualConfigTable, actualTable);
              }
              if (nomalConfigSql.equals(nomalSql)) {
                dataSourceIndex = sqlDsIndexMap.get(configSql);
                break;
              }
            }
          }
        }
      }

      // 1.this case simple handled,just set dataSourceIndex=0
      // 2.default main have 4th priority
      if ((dataSourceIndex == null || dataSourceIndex == NOT_EXIST_USER_SPECIFIED_INDEX)
          && defaultMain) {
        dataSourceIndex = 0;
      }
    }

    // ���ҵ���ֱ��ָ����һ������Դ����ֱ����ָ��������Դ�Ͻ��в�ѯ���²�����ʧ��ʱ�������ԡ�
    if (dataSourceIndex != null
        && dataSourceIndex != NOT_EXIST_USER_SPECIFIED_INDEX) {
      DataSourceHolder dsHolder = findDataSourceWrapperByIndex(dataSourceIndex);
      if (dsHolder == null) {
        throw new IllegalArgumentException("�Ҳ����������Ϊ '"
            + dataSourceIndex + "'������Դ");
      }
      return tryOnDataSourceHolder(dsHolder, failedDataSources, tryer,
          times, args);
    } else {
      return tryExecuteInternal(failedDataSources, tryer, times, args);
    }
  }

  public <T> T tryExecute(DataSourceTryer<T> tryer, int times, Object... args)
      throws SQLException {
    return this.tryExecute(new LinkedHashMap<DataSource, SQLException>(0),
        tryer, times, args);
  }

  public DBType getDbType() {
    return dbType;
  }

  public void setDbType(DBType dbType) {
    this.dbType = dbType;
    this.exceptionSorter = exceptionSorters.get(this.dbType);
  }

  public final void setExceptionSorter(ExceptionSorter exceptionSorter) {
    // add by shenxun:��Ҫ���Ƿ�����ԡ�����������dbSelector�ṹ̫����
    this.exceptionSorter = exceptionSorter;
  }

  public String getId() {
    return id;
  }

  // public abstract DataSource findDataSourceByIndex(int dataSourceIndex);

  protected abstract DataSourceHolder findDataSourceWrapperByIndex(
      int dataSourceIndex);

  protected <T> T tryExecuteInternal(DataSourceTryer<T> tryer, int times,
      Object... args) throws SQLException {
    return this.tryExecuteInternal(
        new LinkedHashMap<DataSource, SQLException>(0), tryer, times,
        args);
  }

  protected abstract <T> T tryExecuteInternal(
      Map<DataSource, SQLException> failedDataSources,
      DataSourceTryer<T> tryer, int times, Object... args)
      throws SQLException;
}
TOP

Related Classes of com.taobao.tddl.jdbc.group.dbselector.AbstractDBSelector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.