package com.taobao.tddl.atom.config;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.lang.BooleanUtils;
import com.taobao.tddl.atom.securety.impl.PasswordCoder;
import com.taobao.tddl.atom.utils.ConnRestrictEntry;
import com.taobao.tddl.common.utils.TStringUtil;
import com.taobao.tddl.common.utils.logger.Logger;
import com.taobao.tddl.common.utils.logger.LoggerFactory;
/**
* TAtom数据源的推送配置解析类
*
* @author qihao
*/
public class TAtomConfParser {
private static Logger logger = LoggerFactory.getLogger(TAtomConfParser.class);
public static final String GLOBA_IP_KEY = "ip";
public static final String GLOBA_PORT_KEY = "port";
public static final String GLOBA_DB_NAME_KEY = "dbName";
public static final String GLOBA_DB_TYPE_KEY = "dbType";
public static final String GLOBA_DB_STATUS_KEY = "dbStatus";
public static final String APP_USER_NAME_KEY = "userName";
public static final String APP_INIT_POOL_SIZE_KEY = "initPoolSize";
public static final String APP_PREFILL = "prefill";
public static final String APP_MIN_POOL_SIZE_KEY = "minPoolSize";
public static final String APP_MAX_POOL_SIZE_KEY = "maxPoolSize";
public static final String APP_IDLE_TIMEOUT_KEY = "idleTimeout";
public static final String APP_BLOCKING_TIMEOUT_KEY = "blockingTimeout";
public static final String APP_PREPARED_STATEMENT_CACHE_SIZE_KEY = "preparedStatementCacheSize";
public static final String APP_ORACLE_CON_TYPE_KEY = "oracleConType";
public static final String APP_CON_PROP_KEY = "connectionProperties";
public static final String PASSWD_ENC_PASSWD_KEY = "encPasswd";
public static final String PASSWD_ENC_KEY_KEY = "encKey";
public static final String APP_DRIVER_CLASS_KEY = "driverClass";
/**
* 写,次数限制
*/
public static final String APP_WRITE_RESTRICT_TIMES = "writeRestrictTimes";
/**
* 读,次数限制
*/
public static final String APP_READ_RESTRICT_TIMES = "readRestrictTimes";
/**
* thread count 次数限制
*/
public static final String APP_THREAD_COUNT_RESTRICT = "threadCountRestrict";
public static final String APP_TIME_SLICE_IN_MILLS = "timeSliceInMillis";
/**
* 应用连接限制: 限制某个应用键值的并发连接数。
*/
public static final String APP_CONN_RESTRICT = "connRestrict";
public static TAtomDsConfDO parserTAtomDsConfDO(String globaConfStr, String appConfStr) {
TAtomDsConfDO pasObj = new TAtomDsConfDO();
if (TStringUtil.isNotBlank(globaConfStr)) {
Properties globaProp = TAtomConfParser.parserConfStr2Properties(globaConfStr);
if (!globaProp.isEmpty()) {
String ip = TStringUtil.trim(globaProp.getProperty(TAtomConfParser.GLOBA_IP_KEY));
if (TStringUtil.isNotBlank(ip)) {
pasObj.setIp(ip);
}
String port = TStringUtil.trim(globaProp.getProperty(TAtomConfParser.GLOBA_PORT_KEY));
if (TStringUtil.isNotBlank(port)) {
pasObj.setPort(port);
}
String dbName = TStringUtil.trim(globaProp.getProperty(TAtomConfParser.GLOBA_DB_NAME_KEY));
if (TStringUtil.isNotBlank(dbName)) {
pasObj.setDbName(dbName);
}
String dbType = TStringUtil.trim(globaProp.getProperty(TAtomConfParser.GLOBA_DB_TYPE_KEY));
if (TStringUtil.isNotBlank(dbType)) {
pasObj.setDbType(dbType);
}
String dbStatus = TStringUtil.trim(globaProp.getProperty(TAtomConfParser.GLOBA_DB_STATUS_KEY));
if (TStringUtil.isNotBlank(dbStatus)) {
pasObj.setDbStatus(dbStatus);
}
}
}
if (TStringUtil.isNotBlank(appConfStr)) {
Properties appProp = TAtomConfParser.parserConfStr2Properties(appConfStr);
if (!appProp.isEmpty()) {
String userName = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_USER_NAME_KEY));
if (TStringUtil.isNotBlank(userName)) {
pasObj.setUserName(userName);
}
String oracleConType = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_ORACLE_CON_TYPE_KEY));
if (TStringUtil.isNotBlank(oracleConType)) {
pasObj.setOracleConType(oracleConType);
}
String initPoolSize = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_INIT_POOL_SIZE_KEY));
if (TStringUtil.isNotBlank(initPoolSize) && TStringUtil.isNumeric(initPoolSize)) {
pasObj.setInitPoolSize(Integer.valueOf(initPoolSize));
}
String minPoolSize = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_MIN_POOL_SIZE_KEY));
if (TStringUtil.isNotBlank(minPoolSize) && TStringUtil.isNumeric(minPoolSize)) {
pasObj.setMinPoolSize(Integer.valueOf(minPoolSize));
}
String maxPoolSize = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_MAX_POOL_SIZE_KEY));
if (TStringUtil.isNotBlank(maxPoolSize) && TStringUtil.isNumeric(maxPoolSize)) {
pasObj.setMaxPoolSize(Integer.valueOf(maxPoolSize));
}
String idleTimeout = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_IDLE_TIMEOUT_KEY));
if (TStringUtil.isNotBlank(idleTimeout) && TStringUtil.isNumeric(idleTimeout)) {
pasObj.setIdleTimeout(Long.valueOf(idleTimeout));
}
String blockingTimeout = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_BLOCKING_TIMEOUT_KEY));
if (TStringUtil.isNotBlank(blockingTimeout) && TStringUtil.isNumeric(blockingTimeout)) {
pasObj.setBlockingTimeout(Integer.valueOf(blockingTimeout));
}
String preparedStatementCacheSize = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_PREPARED_STATEMENT_CACHE_SIZE_KEY));
if (TStringUtil.isNotBlank(preparedStatementCacheSize)
&& TStringUtil.isNumeric(preparedStatementCacheSize)) {
pasObj.setPreparedStatementCacheSize(Integer.valueOf(preparedStatementCacheSize));
}
String writeRestrictTimes = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_WRITE_RESTRICT_TIMES));
if (TStringUtil.isNotBlank(writeRestrictTimes) && TStringUtil.isNumeric(writeRestrictTimes)) {
pasObj.setWriteRestrictTimes(Integer.valueOf(writeRestrictTimes));
}
String readRestrictTimes = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_READ_RESTRICT_TIMES));
if (TStringUtil.isNotBlank(readRestrictTimes) && TStringUtil.isNumeric(readRestrictTimes)) {
pasObj.setReadRestrictTimes(Integer.valueOf(readRestrictTimes));
}
String threadCountRestrict = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_THREAD_COUNT_RESTRICT));
if (TStringUtil.isNotBlank(threadCountRestrict) && TStringUtil.isNumeric(threadCountRestrict)) {
pasObj.setThreadCountRestrict(Integer.valueOf(threadCountRestrict));
}
String timeSliceInMillis = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_TIME_SLICE_IN_MILLS));
if (TStringUtil.isNotBlank(timeSliceInMillis) && TStringUtil.isNumeric(timeSliceInMillis)) {
pasObj.setTimeSliceInMillis(Integer.valueOf(timeSliceInMillis));
}
String conPropStr = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_CON_PROP_KEY));
Map<String, String> connectionProperties = parserConPropStr2Map(conPropStr);
if (null != connectionProperties && !connectionProperties.isEmpty()) {
pasObj.setConnectionProperties(connectionProperties);
String driverClass = connectionProperties.get(TAtomConfParser.APP_DRIVER_CLASS_KEY);
if (!TStringUtil.isBlank(driverClass)) {
pasObj.setDriverClass(driverClass);
}
if (connectionProperties.containsKey(APP_PREFILL)) {
// add by agapple, 简单处理支持下初始化链接
String prefill = connectionProperties.get(APP_PREFILL);
if (BooleanUtils.toBoolean(prefill)
&& pasObj.getInitPoolSize() == TAtomDsConfDO.defaultInitPoolSize) {
pasObj.setInitPoolSize(pasObj.getMinPoolSize());
}
}
}
// 解析应用连接限制, 参看下面的文档
String connRestrictStr = TStringUtil.trim(appProp.getProperty(TAtomConfParser.APP_CONN_RESTRICT));
List<ConnRestrictEntry> connRestrictEntries = parseConnRestrictEntries(connRestrictStr,
pasObj.getMaxPoolSize());
if (null != connRestrictEntries && !connRestrictEntries.isEmpty()) {
pasObj.setConnRestrictEntries(connRestrictEntries);
}
}
}
return pasObj;
}
public static Map<String, String> parserConPropStr2Map(String conPropStr) {
Map<String, String> connectionProperties = null;
if (TStringUtil.isNotBlank(conPropStr)) {
String[] keyValues = TStringUtil.split(conPropStr, ";");
if (null != keyValues && keyValues.length > 0) {
connectionProperties = new HashMap<String, String>(keyValues.length);
for (String keyValue : keyValues) {
String key = TStringUtil.substringBefore(keyValue, "=");
String value = TStringUtil.substringAfter(keyValue, "=");
if (TStringUtil.isNotBlank(key) && TStringUtil.isNotBlank(value)) {
connectionProperties.put(key, value);
}
}
}
}
return connectionProperties;
}
public static String parserPasswd(String passwdStr) {
String passwd = null;
Properties passwdProp = TAtomConfParser.parserConfStr2Properties(passwdStr);
String encPasswd = passwdProp.getProperty(TAtomConfParser.PASSWD_ENC_PASSWD_KEY);
if (TStringUtil.isNotBlank(encPasswd)) {
String encKey = passwdProp.getProperty(TAtomConfParser.PASSWD_ENC_KEY_KEY);
try {
passwd = new PasswordCoder().decode(encKey, encPasswd);
} catch (Exception e) {
logger.error("[parserPasswd Error] decode dbPasswdError!may jdk version error!", e);
}
}
return passwd;
}
public static Properties parserConfStr2Properties(String data) {
Properties prop = new Properties();
if (TStringUtil.isNotBlank(data)) {
ByteArrayInputStream byteArrayInputStream = null;
try {
byteArrayInputStream = new ByteArrayInputStream((data).getBytes());
prop.load(byteArrayInputStream);
} catch (IOException e) {
logger.error("parserConfStr2Properties Error", e);
} finally {
try {
byteArrayInputStream.close();
} catch (IOException e) {
logger.error("parserConfStr2Properties Error", e);
}
}
}
return prop;
}
/**
* HASH 策略的最大槽数量限制。
*/
public static final int MAX_HASH_RESTRICT_SLOT = 32;
/**
* 解析应用连接限制, 完整格式是:
* "K1,K2,K3,K4:80%; K5,K6,K7,K8:80%; K9,K10,K11,K12:80%; *:16,80%; ~:80%;"
* 这样可以兼容 HASH: "*:16,80%", 也可以兼容 LIST:
* "K1:80%; K2:80%; K3:80%; K4:80%; ~:80%;" 配置可以是连接数, 也可以是百分比。
*/
public static List<ConnRestrictEntry> parseConnRestrictEntries(String connRestrictStr, int maxPoolSize) {
List<ConnRestrictEntry> connRestrictEntries = null;
if (TStringUtil.isNotBlank(connRestrictStr)) {
// Split "K1:number1; K2:number2; ...; *:count,number3; ~:number4"
String[] entries = TStringUtil.split(connRestrictStr, ";");
if (null != entries && entries.length > 0) {
HashMap<String, String> existKeys = new HashMap<String, String>();
connRestrictEntries = new ArrayList<ConnRestrictEntry>(entries.length);
for (String entry : entries) {
// Parse "K1,K2,K3:number | *:count,number | ~:number"
int find = entry.indexOf(':');
if (find >= 1 && find < (entry.length() - 1)) {
String key = entry.substring(0, find).trim();
String value = entry.substring(find + 1).trim();
// "K1,K2,K3:number | *:count,number | ~:number"
ConnRestrictEntry connRestrictEntry = ConnRestrictEntry.parseEntry(key, value, maxPoolSize);
if (connRestrictEntry == null) {
logger.error("[connRestrict Error] parse entry error: " + entry);
} else {
// Remark entry config problem
if (0 >= connRestrictEntry.getLimits()) {
logger.error("[connRestrict Error] connection limit is 0: " + entry);
connRestrictEntry.setLimits(/* 至少允许一个连接 */1);
}
if (ConnRestrictEntry.MAX_HASH_RESTRICT_SLOT < connRestrictEntry.getHashSize()) {
logger.error("[connRestrict Error] hash size exceed maximum: " + entry);
connRestrictEntry.setHashSize(ConnRestrictEntry.MAX_HASH_RESTRICT_SLOT);
}
// Remark Key config confliction
for (String slotKey : connRestrictEntry.getKeys()) {
if (!existKeys.containsKey(slotKey)) {
existKeys.put(slotKey, entry);
} else if (ConnRestrictEntry.isWildcard(slotKey)) {
logger.error("[connRestrict Error] hash config [" + entry + "] conflict with ["
+ existKeys.get(slotKey) + "]");
} else if (ConnRestrictEntry.isNullKey(slotKey)) {
logger.error("[connRestrict Error] null-key config [" + entry + "] conflict with ["
+ existKeys.get(slotKey) + "]");
} else {
logger.error("[connRestrict Error] " + slotKey + " config [" + entry
+ "] conflict with [" + existKeys.get(slotKey) + "]");
}
}
connRestrictEntries.add(connRestrictEntry);
}
} else {
logger.error("[connRestrict Error] unknown entry: " + entry);
}
}
}
}
return connRestrictEntries;
}
}