/* * 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-13) */ package com.alibaba.cobar.config.loader.xml; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.sql.SQLSyntaxErrorException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; 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.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 XMLRuleLoader { private final static String DEFAULT_DTD = "/rule.dtd"; private final static String DEFAULT_XML = "/rule.xml"; private final Map<String, TableRuleConfig> tableRules; private final Set<RuleConfig> rules; private final Map<String, RuleAlgorithm> functions; public XMLRuleLoader(String ruleFile) { this.rules = new HashSet<RuleConfig>(); this.tableRules = new HashMap<String, TableRuleConfig>(); this.functions = new HashMap<String, RuleAlgorithm>(); load(DEFAULT_DTD, ruleFile == null ? DEFAULT_XML : ruleFile); } public XMLRuleLoader() { this(null); } public Map<String, TableRuleConfig> getTableRules() { return (Map<String, TableRuleConfig>) (tableRules.isEmpty() ? Collections.emptyMap() : tableRules); } public Set<RuleConfig> listRuleConfig() { return (Set<RuleConfig>) ((rules == null || rules.isEmpty()) ? Collections.emptySet() : rules); } public Map<String, RuleAlgorithm> getFunctions() { return (Map<String, RuleAlgorithm>) (functions.isEmpty() ? Collections.emptyMap() : functions); } private void load(String dtdFile, String xmlFile) { InputStream dtd = null; InputStream xml = null; try { dtd = XMLRuleLoader.class.getResourceAsStream(dtdFile); xml = XMLRuleLoader.class.getResourceAsStream(xmlFile); Element root = ConfigUtil.getDocument(dtd, xml).getDocumentElement(); loadFunctions(root); loadTableRules(root); } catch (ConfigException e) { throw e; } catch (Exception 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 loadTableRules(Element root) throws SQLSyntaxErrorException { NodeList list = root.getElementsByTagName("tableRule"); for (int i = 0, n = list.getLength(); i < n; ++i) { Node node = list.item(i); if (node instanceof Element) { Element e = (Element) node; String name = e.getAttribute("name"); if (tableRules.containsKey(name)) { throw new ConfigException("table rule " + name + " duplicated!"); } NodeList ruleNodes = e.getElementsByTagName("rule"); int length = ruleNodes.getLength(); List<RuleConfig> ruleList = new ArrayList<RuleConfig>(length); for (int j = 0; j < length; ++j) { RuleConfig rule = loadRule((Element) ruleNodes.item(j)); ruleList.add(rule); rules.add(rule); } tableRules.put(name, new TableRuleConfig(name, ruleList)); } } } private RuleConfig loadRule(Element element) throws SQLSyntaxErrorException { Element columnsEle = ConfigUtil.loadElement(element, "columns"); String[] columns = SplitUtil.split(columnsEle.getTextContent(), ',', true); for (int i = 0; i < columns.length; ++i) { columns[i] = columns[i].toUpperCase(); } Element algorithmEle = ConfigUtil.loadElement(element, "algorithm"); String algorithm = algorithmEle.getTextContent(); return new RuleConfig(columns, algorithm); } private void loadFunctions(Element root) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException { NodeList list = root.getElementsByTagName("function"); for (int i = 0, n = list.getLength(); i < n; ++i) { Node node = list.item(i); if (node instanceof Element) { Element e = (Element) node; String name = e.getAttribute("name"); if (functions.containsKey(name)) { throw new ConfigException("rule function " + name + " duplicated!"); } String clazz = e.getAttribute("class"); RuleAlgorithm function = createFunction(name, clazz); ParameterMapping.mapping(function, ConfigUtil.loadElements(e)); functions.put(name, function); } } } private RuleAlgorithm createFunction(String name, String clazz) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException { Class<?> clz = Class.forName(clazz); if (!RuleAlgorithm.class.isAssignableFrom(clz)) { throw new IllegalArgumentException("rule function must implements " + RuleAlgorithm.class.getName() + ", name=" + name); } Constructor<?> constructor = null; for (Constructor<?> cons : clz.getConstructors()) { Class<?>[] paraClzs = cons.getParameterTypes(); if (paraClzs != null && paraClzs.length == 1) { Class<?> paraClzs1 = paraClzs[0]; if (String.class.isAssignableFrom(paraClzs1)) { constructor = cons; break; } } } if (constructor == null) { throw new ConfigException("function " + name + " with class of " + clazz + " must have a constructor with one parameter: String funcName"); } return (RuleAlgorithm) constructor.newInstance(name); } }