| ///* |
| // * Licensed to the Apache Software Foundation (ASF) under one or more |
| // * contributor license agreements. See the NOTICE file distributed with |
| // * this work for additional information regarding copyright ownership. |
| // * The ASF licenses this file to You 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. |
| // */ |
| //package org.apache.dubbo.admin.common.util; |
| // |
| //import org.apache.dubbo.admin.model.domain.ConditionRoute; |
| //import org.apache.dubbo.common.utils.StringUtils; |
| // |
| //import java.text.ParseException; |
| //import java.util.*; |
| //import java.util.Map.Entry; |
| //import java.util.regex.Matcher; |
| //import java.util.regex.Pattern; |
| // |
| ///** |
| // * Router rule can be divided into two parts, When Condition and Then Condition <br> |
| // * When/Then Confition is expressed in a style of (KV) pair, the V part of the condition pair can contain multiple values (a list) <br> |
| // * The meaning of Rule: If a request matches When Condition, then use Then Condition to filter providers (only providers match Then Condition will be returned). <br> |
| // * The process of using Conditions to match consumers and providers is called `Filter`. |
| // * When Condition are used to filter ConsumersController, while Then Condition are used to filter ProvidersController. |
| // * RouteRule performs like this: If a Consumer matches When Condition, then only return the ProvidersController matches Then Condition. This means RouteRule should be applied to current Consumer and the providers returned are filtered by RouteRule.<br> |
| // * |
| // * An example of ConditionRoute Rule:<code> |
| // * key1 = value11,value12 & key2 = value21 & key2 != value22 => key3 = value3 & key4 = value41,vlaue42 & key5 !=value51 |
| // * </code>。 |
| // * The part before <code>=></code> is called When Condition, it's a KV pair; the follower part is Then Condition, also a KV pair. V part in KV can have more than one value, separated by ','<br><br> |
| // * |
| // * Value object, thread safe. |
| // * |
| // */ |
| //public class RouteRule { |
| // @SuppressWarnings("unchecked") |
| // static RouteRule EMPTY = new RouteRule(Collections.EMPTY_MAP, Collections.EMPTY_MAP); |
| // private static Pattern ROUTE_PATTERN = Pattern.compile("([&!=,]*)\\s*([^&!=,\\s]+)"); |
| // private static Pattern CONDITION_SEPERATOR = Pattern.compile("(.*)=>(.*)"); |
| // private static Pattern VALUE_LIST_SEPARATOR = Pattern.compile("\\s*,\\s*"); |
| // final Map<String, MatchPair> whenCondition; |
| // final Map<String, MatchPair> thenCondition; |
| // private volatile String tostring = null; |
| // |
| // // FIXME |
| // 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: Both When Condition and Then Condition can be null |
| // this.whenCondition = when; |
| // this.thenCondition = then; |
| // } |
| // |
| // public static Map<String, MatchPair> parseRule(String rule) |
| // throws ParseException { |
| // Map<String, MatchPair> condition = new HashMap<String, MatchPair>(); |
| // if (StringUtils.isBlank(rule)) { |
| // return condition; |
| // } |
| // // K-V pair, contains matches part and mismatches part |
| // MatchPair pair = null; |
| // // V part has multiple values |
| // Set<String> values = null; |
| // final Matcher matcher = ROUTE_PATTERN.matcher(rule); |
| // while (matcher.find()) { // match one by one |
| // String separator = matcher.group(1); |
| // String content = matcher.group(2); |
| // // The expression starts |
| // if (separator == null || separator.length() == 0) { |
| // pair = new MatchPair(); |
| // condition.put(content, pair); |
| // } |
| // // The KV starts |
| // else if ("&".equals(separator)) { |
| // if (condition.get(content) == null) { |
| // pair = new MatchPair(); |
| // condition.put(content, pair); |
| // } else { |
| // condition.put(content, pair); |
| // } |
| // |
| // } |
| // // The Value part of KV starts |
| // 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); |
| // } |
| // // The Value part of KV starts |
| // 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); |
| // } |
| // // The Value part of KV has multiple values, separated by ',' |
| // else if (",".equals(separator)) { // separated by ',' |
| // 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; |
| // } |
| // |
| // /** |
| // * Parse the RouteRule as a string into an object. |
| // * |
| // * @throws ParseException RouteRule string format is wrong. The following input conditions, RouteRule are illegal. |
| // * <ul> <li> input is <code>null</code>。 |
| // * <li> input is "" or " "。 |
| // * <li> input Rule doesn't have a When Condition |
| // * <li> input Rule doesn't have a Then Condition |
| // * </ul> |
| // */ |
| // public static RouteRule parse(ConditionRoute conditionRoute) throws ParseException { |
| // if (conditionRoute == null) |
| // throw new ParseException("null conditionRoute!", 0); |
| // |
| // if (conditionRoute.getMatchRule() == null && conditionRoute.getFilterRule() == null) { |
| // return parse(conditionRoute.getRule()); |
| // } |
| // |
| // return parse(conditionRoute == null ? null : conditionRoute.getMatchRule(), conditionRoute == null ? null : conditionRoute.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 This is an wrapper exception for the {@link ParseException} thrown by the {@link #parse (String)} method. |
| // */ |
| // public static RouteRule parseQuitely(ConditionRoute conditionRoute) { |
| // try { |
| // return parse(conditionRoute); |
| // } catch (ParseException e) { |
| // throw new RuntimeException(e); |
| // } |
| // } |
| // |
| // static Map<String, MatchPair> parseNameAndValueListString2Condition(Map<String, String> params, Map<String, String> notParams) { |
| // Map<String, MatchPair> condition = new HashMap<String, 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, 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, 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); |
| // } |
| // |
| // /** |
| // * Replace with the new condition value. |
| // * |
| // * @param copy Replace Base |
| // * @param whenCondition WhenCondition to replace, if Base does not have an item, insert it directly. |
| // * @param thenCondition ThenCondition to replace, if Base has no items, then insert directly. |
| // * @return RouteRule after replacement |
| // */ |
| // 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, MatchPair>(); |
| // when.putAll(copy.getWhenCondition()); |
| // if (whenCondition != null) { |
| // when.putAll(whenCondition); |
| // } |
| // |
| // Map<String, MatchPair> then = new HashMap<String, MatchPair>(); |
| // then.putAll(copy.getThenCondition()); |
| // if (thenCondition != null) { |
| // then.putAll(thenCondition); |
| // } |
| // |
| // return new RouteRule(when, then); |
| // } |
| // |
| // // TODO ToString out of the current list is out of order, should we sort? |
| // 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); |
| // } |
| // } |
| // |
| // /** |
| // * Whether the sample passed the conditions. |
| // * <p> |
| // * If there is a Key in the KV for the sample, there is a corresponding MatchPair, and Value does not pass through MatchPair; {@code false} is returned; otherwise, {@code true} is returned. |
| // * |
| // * @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 Remove such method calls |
| // public static String join(Set<String> valueSet) { |
| // StringBuilder sb = new StringBuilder(128); |
| // join(sb, valueSet); |
| // return sb.toString(); |
| // } |
| // |
| // // TODO At present, the multiple Key of Condition is in disorder. Should we sort it? |
| // 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(); |
| // } |
| // |
| // @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(); |
| // } |
| // |
| // // Automatic generation with 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; |
| // } |
| // |
| // // Automatic generation with 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; |
| // } |
| // |
| // public static class MatchPair { |
| // Set<String> matches = new HashSet<String>(); |
| // Set<String> unmatches = new HashSet<String>(); |
| // private volatile boolean freezed = false; |
| // |
| // 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; |
| // } |
| // |
| // 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); |
| // } |
| // |
| // /** |
| // * Whether a given value is matched by the {@link MatchPair}. |
| // * return {@code false}, if |
| // * <ol> |
| // * <li>value is in unmatches |
| // * <li>matches is not null, but value is not in matches. |
| // * </ol> |
| // * otherwise, return<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()); |
| // } |
| // |
| // // Automatic generation with 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; |
| // } |
| // |
| // // Automatic generation with 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; |
| // } |
| // } |
| //} |