package com.ctrip.platform.dal.dao.configure; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.tomcat.jdbc.pool.PoolProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class DatabasePoolConfigParser implements DatabasePoolConfigConstants { private static final Logger logger = LoggerFactory.getLogger(DatabasePoolConfigParser.class); private static DatabasePoolConfigParser poolConfigParser = new DatabasePoolConfigParser(); private static final String DBPOOL_CONFIG = "datasource.xml"; private static final String LOCATION = "location"; private static final String RESOURCE_NODE = "Datasource"; private static final String NAME = "name"; private static final String USER_NAME = "userName"; private static final String PASSWORD = "password"; private static final String CONNECTION_URL = "connectionUrl"; private static final String DRIVER_CLASS_NAME = "driverClassName"; public static final boolean DEFAULT_TESTWHILEIDLE = false; public static final boolean DEFAULT_TESTONBORROW = false; public static final boolean DEFAULT_TESTONRETURN = false; public static final String DEFAULT_VALIDATIONQUERY = "SELECT 1"; public static final long DEFAULT_VALIDATIONINTERVAL = 30000L; public static final int DEFAULT_TIMEBETWEENEVICTIONRUNSMILLIS = 30000; public static final int DEFAULT_MAXACTIVE = 100; public static final int DEFAULT_MINIDLE = 0; public static final int DEFAULT_MAXWAIT = 10000; public static final int DEFAULT_MAXAGE = 30000; public static final int DEFAULT_INITIALSIZE = 10; public static final int DEFAULT_REMOVEABANDONEDTIMEOUT = 60; public static final boolean DEFAULT_REMOVEABANDONED = true; public static final boolean DEFAULT_LOGABANDONED = true; public static final int DEFAULT_MINEVICTABLEIDLETIMEMILLIS = 30000; public static final String DEFAULT_CONNECTIONPROPERTIES = null; public static final boolean DEFAULT_JMXENABLED = true; public static final String DEFAULT_JDBCINTERCEPTORS = "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;" + "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"; public static final String DEFAULT_VALIDATORCLASSNAME = "com.ctrip.platform.dal.dao.datasource.DataSourceValidator"; private Map<String, DatabasePoolConfig> poolConfigs = new ConcurrentHashMap<String, DatabasePoolConfig>(); private String databaseConfigLocation = null; private boolean datasourceXmlExist = false; private DatabasePoolConfigParser() { try { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (classLoader == null) { classLoader = DatabasePoolConfigParser.class.getClassLoader(); } URL url = classLoader.getResource(DBPOOL_CONFIG); if (url != null) { datasourceXmlExist = true; parse(url.openStream()); logger.info("datasource property will use file :" + url.getFile()); } } catch (Throwable e) { logger.error(e.getMessage(), e); throw new RuntimeException(e); } } public static DatabasePoolConfigParser getInstance() { return poolConfigParser; } public DatabasePoolConfig getDatabasePoolConifg(String name) { return poolConfigs.get(name); } public boolean contains(String name) { return poolConfigs.containsKey(name); } public void addDatabasePoolConifg(String name, DatabasePoolConfig config) { poolConfigs.put(name, config); } public void copyDatabasePoolConifg(String sampleName, String newName) { DatabasePoolConfig oldConfig = poolConfigs.get(sampleName); DatabasePoolConfig newConfig = new DatabasePoolConfig(); newConfig.setName(newName); newConfig.setPoolProperties(oldConfig.getPoolProperties()); newConfig.setMap(new HashMap<>(oldConfig.getMap())); poolConfigs.put(newName, newConfig); } public String getDatabaseConfigLocation() { return databaseConfigLocation; } private void parse(InputStream in) throws Exception { try { Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in); parseDocument(doc); in.close(); } finally { if (in != null) { try { in.close(); } catch (Throwable e1) { logger.warn(e1.getMessage(), e1); } } } } private void parseDocument(Document doc) throws Exception { Element root = doc.getDocumentElement(); if (hasAttribute(root, LOCATION)) { databaseConfigLocation = getAttribute(root, LOCATION); } List<Node> resourceList = getChildNodes(root, RESOURCE_NODE); for (Node resource : resourceList) { DatabasePoolConfig poolConfig = parseResource(resource); poolConfigs.put(poolConfig.getName(), poolConfig); } } private DatabasePoolConfig parseResource(Node resource) { DatabasePoolConfig poolConfig = new DatabasePoolConfig(); poolConfig.setName(getAttribute(resource, NAME)); Map<String, String> map = new HashMap<>(); poolConfig.setMap(map); PoolProperties prop = poolConfig.getPoolProperties(); // The following are key connection parameters, developer do not need to provide them in case the configure // provider is set if (hasAttribute(resource, USER_NAME)) { prop.setUsername(getAttribute(resource, USER_NAME)); } if (hasAttribute(resource, PASSWORD)) { prop.setPassword(getAttribute(resource, PASSWORD)); } if (hasAttribute(resource, CONNECTION_URL)) { prop.setUrl(getAttribute(resource, CONNECTION_URL)); } if (hasAttribute(resource, DRIVER_CLASS_NAME)) { prop.setDriverClassName(getAttribute(resource, DRIVER_CLASS_NAME)); } // The following are common options if (hasAttribute(resource, TESTWHILEIDLE)) { String value = getAttribute(resource, TESTWHILEIDLE); boolean testWhileIdle = Boolean.parseBoolean(value); prop.setTestWhileIdle(testWhileIdle); map.put(TESTWHILEIDLE, value); } if (hasAttribute(resource, TESTONBORROW)) { String value = getAttribute(resource, TESTONBORROW); boolean testOnBorrow = Boolean.parseBoolean(value); prop.setTestOnBorrow(testOnBorrow); map.put(TESTONBORROW, value); } if (hasAttribute(resource, TESTONRETURN)) { String value = getAttribute(resource, TESTONRETURN); boolean testOnReturn = Boolean.parseBoolean(value); prop.setTestOnReturn(testOnReturn); map.put(TESTONRETURN, value); } if (hasAttribute(resource, VALIDATIONQUERY)) { String validationQuery = getAttribute(resource, VALIDATIONQUERY); prop.setValidationQuery(validationQuery); map.put(VALIDATIONQUERY, validationQuery); } if (hasAttribute(resource, VALIDATIONINTERVAL)) { String value = getAttribute(resource, VALIDATIONINTERVAL); long validationInterval = Long.parseLong(value); prop.setValidationInterval(validationInterval); map.put(VALIDATIONINTERVAL, value); } if (hasAttribute(resource, TIMEBETWEENEVICTIONRUNSMILLIS)) { String value = getAttribute(resource, TIMEBETWEENEVICTIONRUNSMILLIS); int timeBetweenEvictionRunsMillis = Integer.parseInt(value); prop.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); map.put(TIMEBETWEENEVICTIONRUNSMILLIS, value); } if (hasAttribute(resource, MAX_AGE)) { String value = getAttribute(resource, MAX_AGE); int maxAge = Integer.parseInt(value); prop.setMaxAge(maxAge); map.put(MAX_AGE, value); } if (hasAttribute(resource, MAXACTIVE)) { String value = getAttribute(resource, MAXACTIVE); int maxActive = Integer.parseInt(value); prop.setMaxActive(maxActive); map.put(MAXACTIVE, value); } if (hasAttribute(resource, MINIDLE)) { String value = getAttribute(resource, MINIDLE); int minIdle = Integer.parseInt(value); prop.setMinIdle(minIdle); map.put(MINIDLE, value); } if (hasAttribute(resource, MAXWAIT)) { String value = getAttribute(resource, MAXWAIT); int maxWait = Integer.parseInt(value); prop.setMaxWait(maxWait); map.put(MAXWAIT, value); } if (hasAttribute(resource, INITIALSIZE)) { String value = getAttribute(resource, INITIALSIZE); int initialSize = Integer.parseInt(value); prop.setInitialSize(initialSize); map.put(INITIALSIZE, value); } if (hasAttribute(resource, REMOVEABANDONEDTIMEOUT)) { String value = getAttribute(resource, REMOVEABANDONEDTIMEOUT); int removeAbandonedTimeout = Integer.parseInt(value); prop.setRemoveAbandonedTimeout(removeAbandonedTimeout); map.put(REMOVEABANDONEDTIMEOUT, value); } if (hasAttribute(resource, REMOVEABANDONED)) { String value = getAttribute(resource, REMOVEABANDONED); boolean removeAbandoned = Boolean.parseBoolean(value); prop.setRemoveAbandoned(removeAbandoned); map.put(REMOVEABANDONED, value); } if (hasAttribute(resource, LOGABANDONED)) { String value = getAttribute(resource, LOGABANDONED); boolean logAbandoned = Boolean.parseBoolean(value); prop.setLogAbandoned(logAbandoned); map.put(LOGABANDONED, value); } if (hasAttribute(resource, MINEVICTABLEIDLETIMEMILLIS)) { String value = getAttribute(resource, MINEVICTABLEIDLETIMEMILLIS); int minEvictableIdleTimeMillis = Integer.parseInt(value); prop.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); map.put(MINEVICTABLEIDLETIMEMILLIS, value); } if (hasAttribute(resource, INIT_SQL)) { String value = getAttribute(resource, INIT_SQL); prop.setInitSQL(value); map.put(INIT_SQL, value); } if (hasAttribute(resource, INIT_SQL2)) { String value = getAttribute(resource, INIT_SQL2); prop.setInitSQL(value); map.put(INIT_SQL2, value); } /** * Special handing for connectionProperties and option. If connectionProperties is not set, we will use option's * value if connectionProperties is set, we will ignore option's value */ if (hasAttribute(resource, CONNECTIONPROPERTIES)) { String value = getAttribute(resource, CONNECTIONPROPERTIES); prop.setConnectionProperties(value); map.put(CONNECTIONPROPERTIES, value); } else { if (hasAttribute(resource, OPTION)) { String value = getAttribute(resource, OPTION); prop.setConnectionProperties(value); map.put(CONNECTIONPROPERTIES, value); } } if (hasAttribute(resource, VALIDATORCLASSNAME)) { String value = getAttribute(resource, VALIDATORCLASSNAME); prop.setValidatorClassName(value); map.put(VALIDATORCLASSNAME, value); } return poolConfig; } private List<Node> getChildNodes(Node node, String name) { List<Node> nodes = new ArrayList<Node>(); NodeList children = node.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { if (!children.item(i).getNodeName().equalsIgnoreCase(name)) continue; nodes.add(children.item(i)); } return nodes; } private boolean hasAttribute(Node node, String attributeName) { return node.getAttributes().getNamedItem(attributeName) != null; } private String getAttribute(Node node, String attributeName) { return node.getAttributes().getNamedItem(attributeName).getNodeValue(); } public boolean isDatasourceXmlExist() { return datasourceXmlExist; } }