/* * Copyright 1999-2012 Alibaba Group. * * 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. */ /** * (created at 2012-6-14) */ package com.alibaba.cobar.config.loader.xml; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.alibaba.cobar.config.loader.SchemaLoader; import com.alibaba.cobar.config.model.DataNodeConfig; import com.alibaba.cobar.config.model.DataSourceConfig; import com.alibaba.cobar.config.model.SchemaConfig; import com.alibaba.cobar.config.model.TableConfig; import com.alibaba.cobar.config.model.rule.RuleAlgorithm; import com.alibaba.cobar.config.model.rule.RuleConfig; import com.alibaba.cobar.config.model.rule.TableRuleConfig; import com.alibaba.cobar.config.util.ConfigException; import com.alibaba.cobar.config.util.ConfigUtil; import com.alibaba.cobar.config.util.ParameterMapping; import com.alibaba.cobar.util.SplitUtil; /** * @author <a href="mailto:shuo.qius@alibaba-inc.com">QIU Shuo</a> */ @SuppressWarnings("unchecked") public class XMLSchemaLoader implements SchemaLoader { private final static String DEFAULT_DTD = "/schema.dtd"; private final static String DEFAULT_XML = "/schema.xml"; private final Map<String, TableRuleConfig> tableRules; private final Set<RuleConfig> rules; private final Map<String, RuleAlgorithm> functions; private final Map<String, DataSourceConfig> dataSources; private final Map<String, DataNodeConfig> dataNodes; private final Map<String, SchemaConfig> schemas; public XMLSchemaLoader(String schemaFile, String ruleFile) { XMLRuleLoader ruleLoader = new XMLRuleLoader(ruleFile); this.rules = ruleLoader.listRuleConfig(); this.tableRules = ruleLoader.getTableRules(); this.functions = ruleLoader.getFunctions(); ruleLoader = null; this.dataSources = new HashMap<String, DataSourceConfig>(); this.dataNodes = new HashMap<String, DataNodeConfig>(); this.schemas = new HashMap<String, SchemaConfig>(); this.load(DEFAULT_DTD, schemaFile == null ? DEFAULT_XML : schemaFile); } public XMLSchemaLoader() { this(null, null); } @Override public Map<String, TableRuleConfig> getTableRules() { return tableRules; } @Override public Map<String, RuleAlgorithm> getFunctions() { return functions; } @Override public Map<String, DataSourceConfig> getDataSources() { return (Map<String, DataSourceConfig>) (dataSources.isEmpty() ? Collections.emptyMap() : dataSources); } @Override public Map<String, DataNodeConfig> getDataNodes() { return (Map<String, DataNodeConfig>) (dataNodes.isEmpty() ? Collections.emptyMap() : dataNodes); } @Override public Map<String, SchemaConfig> getSchemas() { return (Map<String, SchemaConfig>) (schemas.isEmpty() ? Collections.emptyMap() : schemas); } @Override public Set<RuleConfig> listRuleConfig() { return rules; } private void load(String dtdFile, String xmlFile) { InputStream dtd = null; InputStream xml = null; try { dtd = XMLSchemaLoader.class.getResourceAsStream(dtdFile); xml = XMLSchemaLoader.class.getResourceAsStream(xmlFile); Element root = ConfigUtil.getDocument(dtd, xml).getDocumentElement(); loadDataSources(root); loadDataNodes(root); loadSchemas(root); } catch (ConfigException e) { throw e; } catch (Throwable e) { throw new ConfigException(e); } finally { if (dtd != null) { try { dtd.close(); } catch (IOException e) { } } if (xml != null) { try { xml.close(); } catch (IOException e) { } } } } private void loadSchemas(Element root) { NodeList list = root.getElementsByTagName("schema"); for (int i = 0, n = list.getLength(); i < n; i++) { Element schemaElement = (Element) list.item(i); String name = schemaElement.getAttribute("name"); String dataNode = schemaElement.getAttribute("dataNode"); // 在非空的情况下检查dataNode是否存在 if (dataNode != null && dataNode.length() != 0) { checkDataNodeExists(dataNode); } else { dataNode = "";// 确保非空 } String group = "default"; if (schemaElement.hasAttribute("group")) { group = schemaElement.getAttribute("group").trim(); } Map<String, TableConfig> tables = loadTables(schemaElement); if (schemas.containsKey(name)) { throw new ConfigException("schema " + name + " duplicated!"); } boolean keepSqlSchema = false; if (schemaElement.hasAttribute("keepSqlSchema")) { keepSqlSchema = Boolean.parseBoolean(schemaElement.getAttribute("keepSqlSchema").trim()); } schemas.put(name, new SchemaConfig(name, dataNode, group, keepSqlSchema, tables)); } } private Map<String, TableConfig> loadTables(Element node) { Map<String, TableConfig> tables = new HashMap<String, TableConfig>(); NodeList nodeList = node.getElementsByTagName("table"); for (int i = 0; i < nodeList.getLength(); i++) { Element tableElement = (Element) nodeList.item(i); String name = tableElement.getAttribute("name").toUpperCase(); String dataNode = tableElement.getAttribute("dataNode"); TableRuleConfig tableRule = null; if (tableElement.hasAttribute("rule")) { String ruleName = tableElement.getAttribute("rule"); tableRule = tableRules.get(ruleName); if (tableRule == null) { throw new ConfigException("rule " + ruleName + " is not found!"); } } boolean ruleRequired = false; if (tableElement.hasAttribute("ruleRequired")) { ruleRequired = Boolean.parseBoolean(tableElement.getAttribute("ruleRequired")); } String[] tableNames = SplitUtil.split(name, ',', true); for (String tableName : tableNames) { TableConfig table = new TableConfig(tableName, dataNode, tableRule, ruleRequired); checkDataNodeExists(table.getDataNodes()); if (tables.containsKey(table.getName())) { throw new ConfigException("table " + tableName + " duplicated!"); } tables.put(table.getName(), table); } } return tables; } private void checkDataNodeExists(String... nodes) { if (nodes == null || nodes.length < 1) { return; } for (String node : nodes) { if (!dataNodes.containsKey(node)) { throw new ConfigException("dataNode '" + node + "' is not found!"); } } } private void loadDataNodes(Element root) { NodeList list = root.getElementsByTagName("dataNode"); for (int i = 0, n = list.getLength(); i < n; i++) { Element element = (Element) list.item(i); String dnNamePrefix = element.getAttribute("name"); List<DataNodeConfig> confList = new ArrayList<DataNodeConfig>(); try { Element dsElement = findPropertyByName(element, "dataSource"); if (dsElement == null) { throw new NullPointerException("dataNode xml Element with name of " + dnNamePrefix + " has no dataSource Element"); } NodeList dataSourceList = dsElement.getElementsByTagName("dataSourceRef"); String dataSources[][] = new String[dataSourceList.getLength()][]; for (int j = 0, m = dataSourceList.getLength(); j < m; ++j) { Element ref = (Element) dataSourceList.item(j); String dsString = ref.getTextContent(); dataSources[j] = SplitUtil.split(dsString, ',', '$', '-', '[', ']'); } if (dataSources.length <= 0) { throw new ConfigException("no dataSourceRef defined!"); } for (String[] dss : dataSources) { if (dss.length != dataSources[0].length) { throw new ConfigException("dataSource number not equals!"); } } for (int k = 0, limit = dataSources[0].length; k < limit; ++k) { StringBuilder dsString = new StringBuilder(); for (int dsIndex = 0; dsIndex < dataSources.length; ++dsIndex) { if (dsIndex > 0) { dsString.append(','); } dsString.append(dataSources[dsIndex][k]); } DataNodeConfig conf = new DataNodeConfig(); ParameterMapping.mapping(conf, ConfigUtil.loadElements(element)); confList.add(conf); switch (k) { case 0: conf.setName((limit == 1) ? dnNamePrefix : dnNamePrefix + "[" + k + "]"); break; default: conf.setName(dnNamePrefix + "[" + k + "]"); break; } conf.setDataSource(dsString.toString()); } } catch (Exception e) { throw new ConfigException("dataNode " + dnNamePrefix + " define error", e); } for (DataNodeConfig conf : confList) { if (dataNodes.containsKey(conf.getName())) { throw new ConfigException("dataNode " + conf.getName() + " duplicated!"); } dataNodes.put(conf.getName(), conf); } } } private void loadDataSources(Element root) { NodeList list = root.getElementsByTagName("dataSource"); for (int i = 0, n = list.getLength(); i < n; ++i) { Element element = (Element) list.item(i); ArrayList<DataSourceConfig> dscList = new ArrayList<DataSourceConfig>(); String dsNamePrefix = element.getAttribute("name"); try { String dsType = element.getAttribute("type"); Element locElement = findPropertyByName(element, "location"); if (locElement == null) { throw new NullPointerException("dataSource xml Element with name of " + dsNamePrefix + " has no location Element"); } NodeList locationList = locElement.getElementsByTagName("location"); int dsIndex = 0; for (int j = 0, m = locationList.getLength(); j < m; ++j) { String locStr = ((Element) locationList.item(j)).getTextContent(); int colonIndex = locStr.indexOf(':'); int slashIndex = locStr.indexOf('/'); String dsHost = locStr.substring(0, colonIndex).trim(); int dsPort = Integer.parseInt(locStr.substring(colonIndex + 1, slashIndex).trim()); String[] schemas = SplitUtil.split(locStr.substring(slashIndex + 1).trim(), ',', '$', '-'); for (String dsSchema : schemas) { DataSourceConfig dsConf = new DataSourceConfig(); ParameterMapping.mapping(dsConf, ConfigUtil.loadElements(element)); dscList.add(dsConf); switch (dsIndex) { case 0: dsConf.setName(dsNamePrefix); break; case 1: dscList.get(0).setName(dsNamePrefix + "[0]"); default: dsConf.setName(dsNamePrefix + "[" + dsIndex + "]"); } dsConf.setType(dsType); dsConf.setDatabase(dsSchema); dsConf.setHost(dsHost); dsConf.setPort(dsPort); ++dsIndex; } } } catch (Exception e) { throw new ConfigException("dataSource " + dsNamePrefix + " define error", e); } for (DataSourceConfig dsConf : dscList) { if (dataSources.containsKey(dsConf.getName())) { throw new ConfigException("dataSource name " + dsConf.getName() + "duplicated!"); } dataSources.put(dsConf.getName(), dsConf); } } } private static Element findPropertyByName(Element bean, String name) { NodeList propertyList = bean.getElementsByTagName("property"); for (int j = 0, m = propertyList.getLength(); j < m; ++j) { Node node = propertyList.item(j); if (node instanceof Element) { Element p = (Element) node; if (name.equals(p.getAttribute("name"))) { return p; } } } return null; } }