/*
* Copyright 1999-2015 dangdang.com.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* </p>
*/
package com.dangdang.ddframe.rdb.sharding.spring.namespace.parser;
import com.dangdang.ddframe.rdb.sharding.config.common.api.config.AutoIncrementColumnConfig;
import com.dangdang.ddframe.rdb.sharding.config.common.api.config.BindingTableRuleConfig;
import com.dangdang.ddframe.rdb.sharding.config.common.api.config.ShardingRuleConfig;
import com.dangdang.ddframe.rdb.sharding.config.common.api.config.TableRuleConfig;
import com.dangdang.ddframe.rdb.sharding.spring.datasource.SpringShardingDataSource;
import com.dangdang.ddframe.rdb.sharding.spring.namespace.constants.ShardingJdbcDataSourceBeanDefinitionParserTag;
import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* 基于Spring命名空间的数据源解析器.
*
* @author caohao
*/
public class ShardingJdbcDataSourceBeanDefinitionParser extends AbstractBeanDefinitionParser {
@Override
//CHECKSTYLE:OFF
protected AbstractBeanDefinition parseInternal(final Element element, final ParserContext parserContext) {
//CHECKSTYLE:ON
BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(SpringShardingDataSource.class);
factory.addConstructorArgValue(parseShardingRuleConfig(element, parserContext));
factory.addConstructorArgValue(parseProperties(element, parserContext));
return factory.getBeanDefinition();
}
private BeanDefinition parseShardingRuleConfig(final Element element, final ParserContext parserContext) {
Element shardingRuleElement = DomUtils.getChildElementByTagName(element, ShardingJdbcDataSourceBeanDefinitionParserTag.SHARDING_RULE_CONFIG_TAG);
BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(ShardingRuleConfig.class);
factory.addPropertyValue("dataSource", parseDataSources(shardingRuleElement, parserContext));
parseDefaultDataSource(factory, shardingRuleElement);
factory.addPropertyValue("tables", parseTableRulesConfig(shardingRuleElement));
factory.addPropertyValue("bindingTables", parseBindingTablesConfig(shardingRuleElement));
factory.addPropertyValue("defaultDatabaseStrategy", parseDefaultDatabaseStrategyConfig(shardingRuleElement));
factory.addPropertyValue("defaultTableStrategy", parseDefaultTableStrategyConfig(shardingRuleElement));
parseIdGenerator(factory, shardingRuleElement);
return factory.getBeanDefinition();
}
private void parseIdGenerator(final BeanDefinitionBuilder factory, final Element element) {
String idGeneratorClass = element.getAttribute(ShardingJdbcDataSourceBeanDefinitionParserTag.ID_GENERATOR_CLASS);
if (!Strings.isNullOrEmpty(idGeneratorClass)) {
factory.addPropertyValue("idGeneratorClass", idGeneratorClass);
}
}
private Map<String, BeanDefinition> parseDataSources(final Element element, final ParserContext parserContext) {
List<String> dataSources = Splitter.on(",").trimResults().splitToList(element.getAttribute(ShardingJdbcDataSourceBeanDefinitionParserTag.DATA_SOURCES_TAG));
Map<String, BeanDefinition> result = new ManagedMap<>(dataSources.size());
for (String each : dataSources) {
result.put(each, parserContext.getRegistry().getBeanDefinition(each));
}
return result;
}
private void parseDefaultDataSource(final BeanDefinitionBuilder factory, final Element element) {
String defaultDataSource = element.getAttribute(ShardingJdbcDataSourceBeanDefinitionParserTag.DEFAULT_DATA_SOURCE_TAG);
if (!Strings.isNullOrEmpty(defaultDataSource)) {
factory.addPropertyValue("defaultDataSourceName", defaultDataSource);
}
}
private Map<String, BeanDefinition> parseTableRulesConfig(final Element element) {
Element tableRulesElement = DomUtils.getChildElementByTagName(element, ShardingJdbcDataSourceBeanDefinitionParserTag.TABLE_RULES_TAG);
List<Element> tableRuleElements = DomUtils.getChildElementsByTagName(tableRulesElement, ShardingJdbcDataSourceBeanDefinitionParserTag.TABLE_RULE_TAG);
Map<String, BeanDefinition> result = new ManagedMap<>(tableRuleElements.size());
for (Element each : tableRuleElements) {
result.put(each.getAttribute(ShardingJdbcDataSourceBeanDefinitionParserTag.LOGIC_TABLE_ATTRIBUTE), parseTableRuleConfig(each));
}
return result;
}
private BeanDefinition parseTableRuleConfig(final Element tableElement) {
BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(TableRuleConfig.class);
String dynamic = tableElement.getAttribute(ShardingJdbcDataSourceBeanDefinitionParserTag.DYNAMIC_TABLE_ATTRIBUTE);
if (!Strings.isNullOrEmpty(dynamic)) {
factory.addPropertyValue("dynamic", dynamic);
}
String actualTables = tableElement.getAttribute(ShardingJdbcDataSourceBeanDefinitionParserTag.ACTUAL_TABLES_ATTRIBUTE);
if (!Strings.isNullOrEmpty(actualTables)) {
factory.addPropertyValue("actualTables", actualTables);
}
String dataSourceNames = tableElement.getAttribute(ShardingJdbcDataSourceBeanDefinitionParserTag.DATA_SOURCE_NAMES_ATTRIBUTE);
if (!Strings.isNullOrEmpty(dataSourceNames)) {
factory.addPropertyValue("dataSourceNames", dataSourceNames);
}
String databaseStrategy = tableElement.getAttribute(ShardingJdbcDataSourceBeanDefinitionParserTag.DATABASE_STRATEGY_ATTRIBUTE);
if (!Strings.isNullOrEmpty(databaseStrategy)) {
factory.addPropertyReference("databaseStrategy", databaseStrategy);
}
String tableStrategy = tableElement.getAttribute(ShardingJdbcDataSourceBeanDefinitionParserTag.TABLE_STRATEGY_ATTRIBUTE);
if (!Strings.isNullOrEmpty(tableStrategy)) {
factory.addPropertyReference("tableStrategy", tableStrategy);
}
List<Element> autoIncrementColumns = DomUtils.getChildElementsByTagName(tableElement, ShardingJdbcDataSourceBeanDefinitionParserTag.AUTO_INCREMENT_COLUMN);
if (null == autoIncrementColumns || autoIncrementColumns.isEmpty()) {
return factory.getBeanDefinition();
}
factory.addPropertyValue("autoIncrementColumns", Lists.transform(autoIncrementColumns, new Function<Element, AutoIncrementColumnConfig>() {
@Override
public AutoIncrementColumnConfig apply(final Element input) {
AutoIncrementColumnConfig result = new AutoIncrementColumnConfig();
result.setColumnName(input.getAttribute(ShardingJdbcDataSourceBeanDefinitionParserTag.COLUMN_NAME));
result.setColumnIdGeneratorClass(input.getAttribute(ShardingJdbcDataSourceBeanDefinitionParserTag.COLUMN_ID_GENERATOR_CLASS));
return result;
}
}));
return factory.getBeanDefinition();
}
private List<BeanDefinition> parseBindingTablesConfig(final Element element) {
Element bindingTableRulesElement = DomUtils.getChildElementByTagName(element, ShardingJdbcDataSourceBeanDefinitionParserTag.BINDING_TABLE_RULES_TAG);
if (null == bindingTableRulesElement) {
return Collections.emptyList();
}
List<Element> bindingTableRuleElements = DomUtils.getChildElementsByTagName(bindingTableRulesElement, ShardingJdbcDataSourceBeanDefinitionParserTag.BINDING_TABLE_RULE_TAG);
BeanDefinitionBuilder bindingTableRuleFactory = BeanDefinitionBuilder.rootBeanDefinition(BindingTableRuleConfig.class);
List<BeanDefinition> result = new ManagedList<>(bindingTableRuleElements.size());
for (Element bindingTableRuleElement : bindingTableRuleElements) {
bindingTableRuleFactory.addPropertyValue("tableNames", bindingTableRuleElement.getAttribute(ShardingJdbcDataSourceBeanDefinitionParserTag.LOGIC_TABLES_ATTRIBUTE));
result.add(bindingTableRuleFactory.getBeanDefinition());
}
return result;
}
private BeanDefinition parseDefaultDatabaseStrategyConfig(final Element element) {
return parseDefaultStrategyConfig(element, ShardingJdbcDataSourceBeanDefinitionParserTag.DEFAULT_DATABASE_STRATEGY_ATTRIBUTE);
}
private BeanDefinition parseDefaultTableStrategyConfig(final Element element) {
return parseDefaultStrategyConfig(element, ShardingJdbcDataSourceBeanDefinitionParserTag.DEFAULT_TABLE_STRATEGY_ATTRIBUTE);
}
private BeanDefinition parseDefaultStrategyConfig(final Element element, final String attr) {
Element strategyElement = DomUtils.getChildElementByTagName(element, attr);
return null == strategyElement ? null : ShardingJdbcStrategyBeanDefinition.getBeanDefinitionByElement(strategyElement);
}
private Properties parseProperties(final Element element, final ParserContext parserContext) {
Element propsElement = DomUtils.getChildElementByTagName(element, ShardingJdbcDataSourceBeanDefinitionParserTag.PROPS_TAG);
return null == propsElement ? new Properties() : parserContext.getDelegate().parsePropsElement(propsElement);
}
}