/* * Copyright 2014-2015 the original author or authors * * 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 on 2014年3月25日 // $Id$ package com.wplatform.ddal.config.parser; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.wplatform.ddal.config.Configuration; import com.wplatform.ddal.dispatch.rule.RuleColumn; import com.wplatform.ddal.dispatch.rule.RuleExpression; import com.wplatform.ddal.dispatch.rule.TableNode; import com.wplatform.ddal.dispatch.rule.TableRouter; import com.wplatform.ddal.util.New; import com.wplatform.ddal.util.StringUtils; import java.io.InputStream; import java.util.ArrayList; import java.util.List; public class XmlRuleConfigParser { private XPathParser parser; private Configuration configuration; public XmlRuleConfigParser(InputStream inputStream, Configuration configuration) { this(new XPathParser(inputStream, true, new RuleEntityResolver()), configuration); } public XmlRuleConfigParser(XPathParser parser, Configuration configuration) { this.configuration = configuration; this.parser = parser; } public static RuleColumn newRuleColumn(String name, String required, String type) { RuleColumn ruleColumn = new RuleColumn(); if (StringUtils.isNullOrEmpty(name) || name.contains("=")) { throw new ParsingException( " Error parsing rule element in rule XML. Cause: the RuleColumn's name is required , and should " + "in the first place ."); } else { ruleColumn.setName(name); } if (!StringUtils.isNullOrEmpty(required)) { ruleColumn.setRequired(Boolean.valueOf(required)); } if (!StringUtils.isNullOrEmpty(type)) { //ruleColumn.setType(type.toLowerCase()); } return ruleColumn; } public void parse() { configurationElement(parser.evalNode("/ddal-rule")); } private void configurationElement(XNode context) { try { parseTableRule(context.evalNodes("/ddal-rule/tableRouter")); } catch (Exception e) { throw new ParsingException("Error parsing ddal-rule XML . Cause: " + e, e); } } private void parseTableRule(List<XNode> list) throws Exception { for (XNode xNode : list) { String id = xNode.getStringAttribute("id"); if (StringUtils.isNullOrEmpty(id)) { throw new ParsingException( "Error parsing ddal-rule XML . Cause: the id attribute of 'tableRouter' element is required."); } TableRouter routeConfig = new TableRouter(null); routeConfig.setId(id); parseTableRuleChildrenXNode(routeConfig, xNode.getChildren()); configuration.addTemporaryTableRouter(id, routeConfig); } } // 解析<tableRule>标签下的所有子标签 private void parseTableRuleChildrenXNode(TableRouter tableRouter, List<XNode> list) { for (XNode xNode : list) { if ("partition".equals(xNode.getName())) { parsePartition(tableRouter, xNode.getChildren()); } else if ("tableRule".equals(xNode.getName())) { RuleExpression ruleExpr = parseRuleExpression(xNode); if (ruleExpr.getRuleColumns().isEmpty()) { throw new ParsingException("The table router '" + tableRouter.getId() + "' has no sharding column."); } tableRouter.setRuleExpression(ruleExpr); } } } private String getStringBody(XNode xNode) { StringBuilder sb = new StringBuilder(); NodeList children = xNode.getNode().getChildNodes(); for (int i = 0; i < children.getLength(); i++) { XNode child = xNode.newXNode(children.item(i)); String nodeName = child.getNode().getNodeName(); if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) { String data = child.getStringBody(""); sb.append(data); } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { throw new ParsingException("Unknown element <" + nodeName + "> in rule XML ."); } } String body = sb.toString(); return body; } // 解析<rule>标签的内容 public RuleExpression parseRuleExpression(XNode xNode) { String stringBody = getStringBody(xNode); String text = stringBody.replaceAll("\\s", " "); final List<RuleColumn> ruleColumns = new ArrayList<RuleColumn>(); GenericTokenParser parser = new GenericTokenParser("${", "}", new TokenHandler() { @Override public String handleToken(String content) { content = content.replaceAll("\\s", ""); String name = null; String required = null; String type = null; if (content.contains(",")) { String[] properties = content.split(","); name = properties[0]; for (int j = 1; j < properties.length; j++) { String propety = properties[j]; if (propety.contains("required=")) { required = propety.split("required=")[1]; } if (propety.contains("type=")) { type = propety.split("type=")[1]; } } } else { name = content; } ruleColumns.add(newRuleColumn(name, required, type)); return name; } }); String expression = parser.parse(text); RuleExpression rule = new RuleExpression(null); rule.setExpression(expression); rule.setRuleColumns(ruleColumns); return rule; } private void parsePartition(TableRouter tableRouter, List<XNode> list) { List<TableNode> tableNodes = New.arrayList(); for (XNode xNode : list) { String shard = xNode.getStringAttribute("shard"); String suffix = xNode.getStringAttribute("suffix"); shard = shard == null ? null : shard.trim(); suffix = suffix == null ? null : suffix.trim(); if (StringUtils.isNullOrEmpty(shard)) { throw new ParsingException("Error parsing ddal-rule XML. Cause: " + "the shard attribute of 'table' element is required."); } List<String> shards = collectItems(shard); List<String> suffixes = collectItems(suffix); if (suffixes.isEmpty()) { for (String shardItem : shards) { TableNode node = new TableNode(shardItem, null, null); if (tableNodes.contains(node)) { throw new ParsingException("Duplicate " + node + " defined in " + tableRouter.getId() + "'s partition"); } tableNodes.add(node); } } else { for (String shardItem : shards) { for (String suffixItem : suffixes) { TableNode node = new TableNode(shardItem, null, suffixItem); if (tableNodes.contains(node)) { throw new ParsingException("Duplicate " + node + " defined in " + tableRouter.getId() + "'s partition"); } tableNodes.add(node); } } } } tableRouter.setPartition(tableNodes); } /** * @param items * @param shards */ private List<String> collectItems(String items) { List<String> result = New.arrayList(); if (!StringUtils.isNullOrEmpty(items)) { for (String string : items.split(",")) { string = string.trim(); if (StringUtils.isNullOrEmpty(string)) { continue; } if (result.contains(string)) { throw new ParsingException( "Error parsing ddal-rule XML . Duplicate item '" + items + "'"); } result.add(string); } } return result; } }