/** * Project: dubbo.registry.server * * File Created at Oct 18, 2010 * $Id: RouteRule.java 182348 2012-06-27 09:16:58Z tony.chenl $ * * Copyright 1999-2100 Alibaba.com Corporation Limited. * All rights reserved. * * This software is the confidential and proprietary information of * Alibaba Company. ("Confidential Information"). You shall not * disclose such Confidential Information and shall use it only in * accordance with the terms of the license agreement you entered into * with Alibaba.com. */ package com.alibaba.dubbo.registry.common.route; import java.text.ParseException; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.alibaba.dubbo.common.utils.StringUtils; import com.alibaba.dubbo.registry.common.domain.Route; /** * Rule分成两部分,When条件和Then条件。<br> * 条件是一组键值(KV)对,表示匹配条件。条件包含的键值中的“值”(Value)可以有多个值,即是一个列表。<br> * Rule的含义是,符合了When条件后,则进行Then条件的过虑。<br> * 当然被When条件的符合的、被Then条件过滤的,也是一组KV,术语上我们就称为样本(Sample)吧。<br> * 使用条件对样本进行的匹配的过程称为“过滤”(或称为“筛选”)(Filter)。 * 使用When条件过滤和使用Then条件过滤的样本,不需要是相同的集合。如在Dubbo中,分别对应的是Consumer和Provider。 * 对于RouteRule(路由规则)含义即,符合When条件的Consumer,则对Provider进行Then过滤,出来的Provide即是提供给这个Consumer的Provider。<br> * * Rule的字符串格式如下:<code> * key1 = value11,value12 & key2 = value21 & key2 != value22 => key3 = value3 & key4 = value41,vlaue42 & key5 !=value51 * </code>。 * <code>=></code>之前的称为When条件,是KV对;之后是Then条件,是KV对。KV的Value可以有多个值。<br><br> * * 值对象,线程安全。 * * @author william.liangf * @author ding.lid */ public class RouteRule { public static class MatchPair { Set<String> matches = new HashSet<String>(); Set<String> unmatches = new HashSet<String>(); public MatchPair() { } public MatchPair(Set<String> matches, Set<String> unmatches) { if(matches == null || unmatches == null) { throw new IllegalArgumentException("argument of MatchPair is null!"); } this.matches = matches; this.unmatches = unmatches; } public Set<String> getMatches() { return matches; } public Set<String> getUnmatches() { return unmatches; } public MatchPair copy() { MatchPair ret = new MatchPair(); ret.matches.addAll(matches); ret.unmatches.addAll(unmatches); return ret; } private volatile boolean freezed = false; void freeze() { if(freezed) return; synchronized (this) { if(freezed) return; matches = Collections.unmodifiableSet(matches); unmatches = Collections.unmodifiableSet(unmatches); } } public boolean containeValue(String value) { return matches.contains(value) || unmatches.contains(value); } /** * 给定的值是否通过该{@link MatchPair}匹配。<p> * 返回{@code false},如果 * <ol> * <li>value在unmatches列表中 * <li>matches列表有值,但value不在matches列表中。 * </ol> * otherwise返回<code>true</code>。 */ public boolean pass(String sample) { if(unmatches.contains(sample)) return false; if(matches.isEmpty()) return true; return matches.contains(sample); } @Override public String toString() { return String.format("{matches=%s,unmatches=%s}", matches.toString(), unmatches.toString()); } // 用Eclipse自动生成 @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((matches == null) ? 0 : matches.hashCode()); result = prime * result + ((unmatches == null) ? 0 : unmatches.hashCode()); return result; } // 用Eclipse自动生成 @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; MatchPair other = (MatchPair) obj; if (matches == null) { if (other.matches != null) return false; } else if (!matches.equals(other.matches)) return false; if (unmatches == null) { if (other.unmatches != null) return false; } else if (!unmatches.equals(other.unmatches)) return false; return true; } } final Map<String, MatchPair> whenCondition; final Map<String, MatchPair> thenCondition; private static Pattern ROUTE_PATTERN = Pattern.compile("([&!=,]*)\\s*([^&!=,\\s]+)"); private static Pattern CONDITION_SEPERATOR = Pattern.compile("(.*)=>(.*)"); public static Map<String, MatchPair> parseRule(String rule) throws ParseException { Map<String, MatchPair> condition = new HashMap<String, RouteRule.MatchPair>(); if(StringUtils.isBlank(rule)) { return condition; } // 匹配或不匹配Key-Value对 MatchPair pair = null; // 多个Value值 Set<String> values = null; final Matcher matcher = ROUTE_PATTERN.matcher(rule); while (matcher.find()) { // 逐个匹配 String separator = matcher.group(1); String content = matcher.group(2); // 表达式开始 if (separator == null || separator.length() == 0) { pair = new MatchPair(); condition.put(content, pair); } // KV开始 else if ("&".equals(separator)) { if (condition.get(content) == null) { pair = new MatchPair(); condition.put(content, pair); } else { condition.put(content, pair); } } // KV的Value部分开始 else if ("=".equals(separator)) { if (pair == null) throw new ParseException("Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start()); values = pair.matches; values.add(content); } // KV的Value部分开始 else if ("!=".equals(separator)) { if (pair == null) throw new ParseException("Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start()); values = pair.unmatches; values.add(content); } // KV的Value部分的多个条目 else if (",".equals(separator)) { // 如果为逗号表示 if (values == null || values.size() == 0) throw new ParseException("Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start()); values.add(content); } else { throw new ParseException("Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start()); } } return condition; } // FIXME 集合都要加上unmodified的Wrapper,避免构造后的对象被修改 private RouteRule(Map<String, MatchPair> when, Map<String, MatchPair> then) { for(Map.Entry<String, MatchPair> entry : when.entrySet()) { entry.getValue().freeze(); } for(Map.Entry<String, MatchPair> entry : then.entrySet()) { entry.getValue().freeze(); } // NOTE: When条件是允许为空的,外部业务来保证类似的约束条件 this.whenCondition = when; this.thenCondition = then; } @SuppressWarnings("unchecked") static RouteRule EMPTY = new RouteRule(Collections.EMPTY_MAP, Collections.EMPTY_MAP); /** * 把字符串形式的RouteRule的解析成对象。 * * @throws ParseException RouteRule字符串格式不对了。以下输入的情况,RouteRule都是非法的。 * <ul> <li> 输入是<code>null</code>。 * <li> 输入是空串,或是空白串。 * <li> 输入的Rule,没有When条件 * <li> 输入的Rule,没有Then条件 * </ul> */ public static RouteRule parse(Route route) throws ParseException { if(route == null) throw new ParseException("null route!", 0); if(route.getMatchRule() == null && route.getFilterRule() == null) { return parse(route.getRule()); } return parse(route == null ? null : route.getMatchRule(), route == null ? null : route.getFilterRule()); } public static RouteRule parse(String whenRule, String thenRule) throws ParseException { /*if (whenRule == null || whenRule.trim().length() == 0) { throw new ParseException("Illegal route rule without when express", 0); }*/ if (thenRule == null || thenRule.trim().length() == 0) { throw new ParseException("Illegal route rule without then express", 0); } Map<String, MatchPair> when = parseRule(whenRule.trim()); Map<String, MatchPair> then = parseRule(thenRule.trim()); return new RouteRule(when, then); } public static RouteRule parse(String rule) throws ParseException { if(StringUtils.isBlank(rule)) { throw new ParseException("Illegal blank route rule", 0); } final Matcher matcher = CONDITION_SEPERATOR.matcher(rule); if(!matcher.matches()) throw new ParseException("condition seperator => not found!", 0); return parse(matcher.group(1), matcher.group(2)); } /** * @see #parse(String) * @throws RuntimeException 解析出错时,Wrap了{@link #parse(String)}方法的抛出的{@link ParseException}的异常。 */ public static RouteRule parseQuitely(Route route) { try { return parse(route); } catch (ParseException e) { throw new RuntimeException(e); } } private static Pattern VALUE_LIST_SEPARATOR = Pattern.compile("\\s*,\\s*"); static Map<String, MatchPair> parseNameAndValueListString2Condition(Map<String, String> params, Map<String, String> notParams) { Map<String, MatchPair> condition = new HashMap<String, RouteRule.MatchPair>(); for(Entry<String, String> entry : params.entrySet()) { String valueListString = entry.getValue(); if(StringUtils.isBlank(valueListString)) { continue; } String[] list = VALUE_LIST_SEPARATOR.split(valueListString); Set<String> set = new HashSet<String>(); for(String item : list) { if(StringUtils.isBlank(item)) { continue; } set.add(item.trim()); } if(set.isEmpty()) { continue; } String key = entry.getKey(); MatchPair matchPair = condition.get(key); if(null == matchPair) { matchPair = new MatchPair(); condition.put(key, matchPair); } matchPair.matches = set; } for(Entry<String, String> entry : notParams.entrySet()) { String valueListString = entry.getValue(); if(StringUtils.isBlank(valueListString)) { continue; } String[] list = VALUE_LIST_SEPARATOR.split(valueListString); Set<String> set = new HashSet<String>(); for(String item : list) { if(StringUtils.isBlank(item)) { continue; } set.add(item.trim()); } if(set.isEmpty()) { continue; } String key = entry.getKey(); MatchPair matchPair = condition.get(key); if(null == matchPair) { matchPair = new MatchPair(); condition.put(key, matchPair); } matchPair.unmatches = set; } return condition; } public static RouteRule createFromNameAndValueListString(Map<String, String> whenParams, Map<String, String> notWhenParams, Map<String, String> thenParams, Map<String, String> notThenParams) { Map<String, MatchPair> when = parseNameAndValueListString2Condition(whenParams, notWhenParams); Map<String, MatchPair> then = parseNameAndValueListString2Condition(thenParams, notThenParams); return new RouteRule(when, then); } public static RouteRule createFromCondition(Map<String, MatchPair> whenCondition, Map<String, MatchPair> thenCondition) { return new RouteRule(whenCondition, thenCondition); } public static RouteRule copyWithRemove(RouteRule copy, Set<String> whenParams, Set<String> thenParams) { Map<String, MatchPair> when = new HashMap<String, RouteRule.MatchPair>(); for(Entry<String, MatchPair> entry : copy.getWhenCondition().entrySet()) { if(whenParams == null || !whenParams.contains(entry.getKey())) { when.put(entry.getKey(), entry.getValue()); } } Map<String, MatchPair> then = new HashMap<String, RouteRule.MatchPair>(); for(Entry<String, MatchPair> entry : copy.getThenCondition().entrySet()) { if(thenParams ==null || !thenParams.contains(entry.getKey())) { then.put(entry.getKey(), entry.getValue()); } } return new RouteRule(when, then); } /** * 使用新的条件值来替换。 * * @param copy 替换的Base * @param whenCondition 要替换的whenCondition,如果Base没有项目,则直接插入。 * @param thenCondition 要替换的thenCondition,如果Base没有项目,则直接插入。 * @return 替换后的RouteRule */ public static RouteRule copyWithReplace(RouteRule copy, Map<String, MatchPair> whenCondition, Map<String, MatchPair> thenCondition) { if(null == copy) { throw new NullPointerException("Argument copy is null!"); } Map<String, MatchPair> when = new HashMap<String, RouteRule.MatchPair>(); when.putAll(copy.getWhenCondition()); if(whenCondition != null) { when.putAll(whenCondition); } Map<String, MatchPair> then = new HashMap<String, RouteRule.MatchPair>(); then.putAll(copy.getThenCondition()); if(thenCondition != null) { then.putAll(thenCondition); } return new RouteRule(when, then); } // TODO 目前ToString出来的列表是乱序的,是否要排序? static void join(StringBuilder sb, Set<String> valueSet) { boolean isFirst = true; for(String s : valueSet) { if(isFirst) { isFirst = false; } else { sb.append(","); } sb.append(s); } } /** * 样本是否通过条件。 * <p> * 如果样本的KV中,存在Key有对应的MatchPair,且Value不通过MatchPair里,返回{@code false}; * 否则返回{@code true}。 * * @see MatchPair#pass(String) */ public static boolean matchCondition(Map<String, String> sample, Map<String, MatchPair> condition) { for (Map.Entry<String, String> entry : sample.entrySet()) { String key = entry.getKey(); MatchPair pair = condition.get(key); if (pair != null && !pair.pass(entry.getValue())) { return false; } } return true; } // FIXME 去掉这样的方法调用 public static String join(Set<String> valueSet) { StringBuilder sb = new StringBuilder(128); join(sb, valueSet); return sb.toString(); } // TODO 目前Condition的多个Key是乱序的,是否要排序? public static void contidionToString(StringBuilder sb, Map<String, MatchPair> condition) { boolean isFirst = true; for(Entry<String, MatchPair> entry: condition.entrySet()) { String keyName = entry.getKey(); MatchPair p = entry.getValue(); @SuppressWarnings("unchecked") Set<String>[] setArray = new Set[]{p.matches, p.unmatches}; String[] opArray = {" = ", " != "}; for(int i = 0; i < setArray.length; ++i) { if(setArray[i].isEmpty()) { continue; } if(isFirst) { isFirst = false; } else { sb.append(" & "); } sb.append(keyName); sb.append(opArray[i]); join(sb, setArray[i]); } } } public boolean isWhenContainValue(String key, String value) { MatchPair matchPair = whenCondition.get(key); if(null == matchPair) { return false; } return matchPair.containeValue(value); } public boolean isThenContainValue(String key, String value) { MatchPair matchPair = thenCondition.get(key); if(null == matchPair) { return false; } return matchPair.containeValue(value); } public boolean isContainValue(String key, String value) { return isWhenContainValue(key, value) || isThenContainValue(key, value); } public Map<String, MatchPair> getWhenCondition() { return whenCondition; } public Map<String, MatchPair> getThenCondition() { return thenCondition; } public String getWhenConditionString() { StringBuilder sb = new StringBuilder(512); contidionToString(sb, whenCondition); return sb.toString(); } public String getThenConditionString() { StringBuilder sb = new StringBuilder(512); contidionToString(sb, thenCondition); return sb.toString(); } private volatile String tostring = null; @Override public String toString() { if(tostring != null) return tostring; StringBuilder sb = new StringBuilder(512); contidionToString(sb, whenCondition); sb.append(" => "); contidionToString(sb, thenCondition); return tostring = sb.toString(); } // 用Eclipse自动生成 @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((thenCondition == null) ? 0 : thenCondition.hashCode()); result = prime * result + ((whenCondition == null) ? 0 : whenCondition.hashCode()); return result; } // 用Eclipse自动生成 @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; RouteRule other = (RouteRule) obj; if (thenCondition == null) { if (other.thenCondition != null) return false; } else if (!thenCondition.equals(other.thenCondition)) return false; if (whenCondition == null) { if (other.whenCondition != null) return false; } else if (!whenCondition.equals(other.whenCondition)) return false; return true; } }