Update parameter flow rule to adapt to cluster mode and extract rule util class

- Update ParamFlowRule to support cluster mode
- Add `ParamFlowClusterConfig` to provide cluster mode items for the rule
- Update ParamFlowChecker to support cluster flow mode
- Extract ParamFlowRuleUtil class
- Change type of `flowId` from Integer to Long

Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
Eric Zhao 2018-11-21 19:02:21 +08:00
parent a6534e5b13
commit f539b5b827
10 changed files with 419 additions and 166 deletions

View File

@ -33,7 +33,7 @@ public interface TokenService {
* @param prioritized whether the request is prioritized
* @return result of the token request
*/
TokenResult requestToken(Integer ruleId, int acquireCount, boolean prioritized);
TokenResult requestToken(Long ruleId, int acquireCount, boolean prioritized);
/**
* Request tokens for a specific parameter from remote token server.
@ -43,5 +43,5 @@ public interface TokenService {
* @param params parameter list
* @return result of the token request
*/
TokenResult requestParamToken(Integer ruleId, int acquireCount, Collection<Object> params);
TokenResult requestParamToken(Long ruleId, int acquireCount, Collection<Object> params);
}

View File

@ -28,7 +28,7 @@ public class ClusterFlowConfig {
/**
* Global unique ID.
*/
private Integer flowId;
private Long flowId;
/**
* Threshold type (average by local value or global value).
@ -41,15 +41,15 @@ public class ClusterFlowConfig {
*/
private int strategy = ClusterRuleConstant.FLOW_CLUSTER_STRATEGY_NORMAL;
private Integer refFlowId;
private Long refFlowId;
private int refSampleCount = 10;
private double refRatio = 1d;
public Integer getFlowId() {
public Long getFlowId() {
return flowId;
}
public ClusterFlowConfig setFlowId(Integer flowId) {
public ClusterFlowConfig setFlowId(Long flowId) {
this.flowId = flowId;
return this;
}
@ -72,11 +72,11 @@ public class ClusterFlowConfig {
return this;
}
public Integer getRefFlowId() {
public Long getRefFlowId() {
return refFlowId;
}
public ClusterFlowConfig setRefFlowId(Integer refFlowId) {
public ClusterFlowConfig setRefFlowId(Long refFlowId) {
this.refFlowId = refFlowId;
return this;
}

View File

@ -155,7 +155,7 @@ public final class FlowRuleUtil {
* @param id flow ID to check
* @return true if valid, otherwise false
*/
public static boolean validClusterRuleId(Integer id) {
public static boolean validClusterRuleId(Long id) {
return id != null && id > 0;
}

View File

@ -16,14 +16,23 @@
package com.alibaba.csp.sentinel.slots.block.flow.param;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import com.alibaba.csp.sentinel.cluster.ClusterTokenClient;
import com.alibaba.csp.sentinel.cluster.TokenClientProvider;
import com.alibaba.csp.sentinel.cluster.TokenResult;
import com.alibaba.csp.sentinel.cluster.TokenResultStatus;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
/**
* Rule checker for parameter flow control.
*
* @author Eric Zhao
* @since 0.2.0
*/
@ -40,17 +49,72 @@ final class ParamFlowChecker {
return true;
}
// Get parameter value. If value is null, then pass.
Object value = args[paramIdx];
if (value == null) {
return true;
}
if (rule.isClusterMode()) {
return passClusterCheck(resourceWrapper, rule, count, value);
}
return passLocalCheck(resourceWrapper, rule, count, value);
}
private static ParameterMetric getHotParameters(ResourceWrapper resourceWrapper) {
// Should not be null.
return ParamFlowSlot.getParamMetric(resourceWrapper);
@SuppressWarnings("unchecked")
private static Collection<Object> toCollection(Object value) {
if (value instanceof Collection) {
return (Collection<Object>)value;
} else if (value.getClass().isArray()) {
List<Object> params = new ArrayList<Object>();
int length = Array.getLength(value);
for (int i = 0; i < length; i++) {
Object param = Array.get(value, i);
params.add(param);
}
return params;
} else {
return Collections.singletonList(value);
}
}
private static boolean passLocalCheck(ResourceWrapper resourceWrapper, ParamFlowRule rule, int count, Object value) {
private static boolean passClusterCheck(ResourceWrapper resourceWrapper, ParamFlowRule rule, int count,
Object value) {
try {
ClusterTokenClient client = TokenClientProvider.getClient();
if (client == null) {
return true;
}
Collection<Object> params = toCollection(value);
TokenResult result = client.requestParamToken(rule.getClusterConfig().getFlowId(), count, params);
switch (result.getStatus()) {
case TokenResultStatus.OK:
return true;
case TokenResultStatus.BLOCKED:
return false;
default:
return fallbackToLocalOrPass(resourceWrapper, rule, count, params);
}
} catch (Throwable ex) {
RecordLog.warn("[ParamFlowChecker] Request cluster token for parameter unexpected failed", ex);
return fallbackToLocalOrPass(resourceWrapper, rule, count, value);
}
}
private static boolean fallbackToLocalOrPass(ResourceWrapper resourceWrapper, ParamFlowRule rule, int count,
Object value) {
if (rule.getClusterConfig().isFallbackToLocalWhenFail()) {
return passLocalCheck(resourceWrapper, rule, count, value);
} else {
// The rule won't be activated, just pass.
return true;
}
}
private static boolean passLocalCheck(ResourceWrapper resourceWrapper, ParamFlowRule rule, int count,
Object value) {
try {
if (Collection.class.isAssignableFrom(value.getClass())) {
for (Object param : ((Collection)value)) {
@ -70,7 +134,7 @@ final class ParamFlowChecker {
return passSingleValueCheck(resourceWrapper, rule, count, value);
}
} catch (Throwable e) {
RecordLog.info("[ParamFlowChecker] Unexpected error", e);
RecordLog.warn("[ParamFlowChecker] Unexpected error", e);
}
return true;
@ -96,5 +160,10 @@ final class ParamFlowChecker {
return true;
}
private static ParameterMetric getHotParameters(ResourceWrapper resourceWrapper) {
// Should not be null.
return ParamFlowSlot.getParamMetric(resourceWrapper);
}
private ParamFlowChecker() {}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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.
*/
package com.alibaba.csp.sentinel.slots.block.flow.param;
import com.alibaba.csp.sentinel.slots.block.ClusterRuleConstant;
/**
* Parameter flow rule config in cluster mode.
*
* @author Eric Zhao
* @since 1.4.0
*/
public class ParamFlowClusterConfig {
/**
* Global unique ID.
*/
private Long flowId;
/**
* Threshold type (average by local value or global value).
*/
private int thresholdType = ClusterRuleConstant.FLOW_THRESHOLD_AVG_LOCAL;
private boolean fallbackToLocalWhenFail = false;
public Long getFlowId() {
return flowId;
}
public ParamFlowClusterConfig setFlowId(Long flowId) {
this.flowId = flowId;
return this;
}
public int getThresholdType() {
return thresholdType;
}
public ParamFlowClusterConfig setThresholdType(int thresholdType) {
this.thresholdType = thresholdType;
return this;
}
public boolean isFallbackToLocalWhenFail() {
return fallbackToLocalWhenFail;
}
public ParamFlowClusterConfig setFallbackToLocalWhenFail(boolean fallbackToLocalWhenFail) {
this.fallbackToLocalWhenFail = fallbackToLocalWhenFail;
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
ParamFlowClusterConfig that = (ParamFlowClusterConfig)o;
if (thresholdType != that.thresholdType) { return false; }
if (fallbackToLocalWhenFail != that.fallbackToLocalWhenFail) { return false; }
return flowId != null ? flowId.equals(that.flowId) : that.flowId == null;
}
@Override
public int hashCode() {
int result = flowId != null ? flowId.hashCode() : 0;
result = 31 * result + thresholdType;
result = 31 * result + (fallbackToLocalWhenFail ? 1 : 0);
return result;
}
@Override
public String toString() {
return "ParamFlowClusterConfig{" +
"flowId=" + flowId +
", thresholdType=" + thresholdType +
", fallbackToLocalWhenFail=" + fallbackToLocalWhenFail +
'}';
}
}

View File

@ -65,6 +65,9 @@ public class ParamFlowRule extends AbstractRule {
*/
private Map<Object, Integer> hotItems = new HashMap<Object, Integer>();
private boolean clusterMode = false;
private ParamFlowClusterConfig clusterConfig;
public int getGrade() {
return grade;
}
@ -110,6 +113,25 @@ public class ParamFlowRule extends AbstractRule {
return this;
}
public boolean isClusterMode() {
return clusterMode;
}
public ParamFlowRule setClusterMode(boolean clusterMode) {
this.clusterMode = clusterMode;
return this;
}
public ParamFlowClusterConfig getClusterConfig() {
return clusterConfig;
}
public ParamFlowRule setClusterConfig(
ParamFlowClusterConfig clusterConfig) {
this.clusterConfig = clusterConfig;
return this;
}
@Override
@Deprecated
public boolean passCheck(Context context, DefaultNode node, int count, Object... args) {
@ -126,8 +148,11 @@ public class ParamFlowRule extends AbstractRule {
if (grade != rule.grade) { return false; }
if (Double.compare(rule.count, count) != 0) { return false; }
if (clusterMode != rule.clusterMode) { return false; }
if (paramIdx != null ? !paramIdx.equals(rule.paramIdx) : rule.paramIdx != null) { return false; }
return paramFlowItemList != null ? paramFlowItemList.equals(rule.paramFlowItemList) : rule.paramFlowItemList == null;
if (paramFlowItemList != null ? !paramFlowItemList.equals(rule.paramFlowItemList)
: rule.paramFlowItemList != null) { return false; }
return clusterConfig != null ? clusterConfig.equals(rule.clusterConfig) : rule.clusterConfig == null;
}
@Override
@ -139,18 +164,20 @@ public class ParamFlowRule extends AbstractRule {
temp = Double.doubleToLongBits(count);
result = 31 * result + (int)(temp ^ (temp >>> 32));
result = 31 * result + (paramFlowItemList != null ? paramFlowItemList.hashCode() : 0);
result = 31 * result + (clusterMode ? 1 : 0);
result = 31 * result + (clusterConfig != null ? clusterConfig.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "ParamFlowRule{" +
"resource=" + getResource() +
", limitApp=" + getLimitApp() +
", grade=" + grade +
"grade=" + grade +
", paramIdx=" + paramIdx +
", count=" + count +
", paramFlowItemList=" + paramFlowItemList +
", clusterMode=" + clusterMode +
", clusterConfig=" + clusterConfig +
'}';
}
}

View File

@ -99,37 +99,6 @@ public final class ParamFlowRuleManager {
return rules;
}
private static Object parseValue(String value, String classType) {
if (value == null) {
throw new IllegalArgumentException("Null value");
}
if (StringUtil.isBlank(classType)) {
// If the class type is not provided, then treat it as string.
return value;
}
// Handle primitive type.
if (int.class.toString().equals(classType) || Integer.class.getName().equals(classType)) {
return Integer.parseInt(value);
} else if (boolean.class.toString().equals(classType) || Boolean.class.getName().equals(classType)) {
return Boolean.parseBoolean(value);
} else if (long.class.toString().equals(classType) || Long.class.getName().equals(classType)) {
return Long.parseLong(value);
} else if (double.class.toString().equals(classType) || Double.class.getName().equals(classType)) {
return Double.parseDouble(value);
} else if (float.class.toString().equals(classType) || Float.class.getName().equals(classType)) {
return Float.parseFloat(value);
} else if (byte.class.toString().equals(classType) || Byte.class.getName().equals(classType)) {
return Byte.parseByte(value);
} else if (short.class.toString().equals(classType) || Short.class.getName().equals(classType)) {
return Short.parseShort(value);
} else if (char.class.toString().equals(classType)) {
char[] array = value.toCharArray();
return array.length > 0 ? array[0] : null;
}
return value;
}
static class RulePropertyListener implements PropertyListener<List<ParamFlowRule>> {
@Override
@ -163,7 +132,7 @@ public final class ParamFlowRuleManager {
}
for (ParamFlowRule rule : list) {
if (!isValidRule(rule)) {
if (!ParamFlowRuleUtil.isValidRule(rule)) {
RecordLog.warn("[ParamFlowRuleManager] Ignoring invalid rule when loading new rules: " + rule);
continue;
}
@ -172,12 +141,7 @@ public final class ParamFlowRuleManager {
rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
}
if (rule.getParamFlowItemList() == null) {
rule.setParamFlowItemList(new ArrayList<ParamFlowItem>());
}
Map<Object, Integer> itemMap = parseHotItems(rule.getParamFlowItemList());
rule.setParsedHotItems(itemMap);
ParamFlowRuleUtil.fillExceptionFlowItems(rule);
String resourceName = rule.getResource();
List<ParamFlowRule> ruleList = newRuleMap.get(resourceName);
@ -200,34 +164,6 @@ public final class ParamFlowRuleManager {
}
}
static Map<Object, Integer> parseHotItems(List<ParamFlowItem> items) {
Map<Object, Integer> itemMap = new HashMap<Object, Integer>();
if (items == null || items.isEmpty()) {
return itemMap;
}
for (ParamFlowItem item : items) {
// Value should not be null.
Object value;
try {
value = parseValue(item.getObject(), item.getClassType());
} catch (Exception ex) {
RecordLog.warn("[ParamFlowRuleManager] Failed to parse value for item: " + item, ex);
continue;
}
if (item.getCount() == null || item.getCount() < 0 || value == null) {
RecordLog.warn("[ParamFlowRuleManager] Ignoring invalid exclusion parameter item: " + item);
continue;
}
itemMap.put(value, item.getCount());
}
return itemMap;
}
static boolean isValidRule(ParamFlowRule rule) {
return rule != null && !StringUtil.isBlank(rule.getResource()) && rule.getCount() >= 0
&& rule.getParamIdx() != null && rule.getParamIdx() >= 0;
}
private ParamFlowRuleManager() {}
}

View File

@ -0,0 +1,114 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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.
*/
package com.alibaba.csp.sentinel.slots.block.flow.param;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.util.StringUtil;
/**
* @author Eric Zhao
*/
public final class ParamFlowRuleUtil {
public static boolean isValidRule(ParamFlowRule rule) {
return rule != null && !StringUtil.isBlank(rule.getResource()) && rule.getCount() >= 0
&& rule.getParamIdx() != null && rule.getParamIdx() >= 0 && checkCluster(rule);
}
private static boolean checkCluster(/*@PreChecked*/ ParamFlowRule rule) {
if (!rule.isClusterMode()) {
return true;
}
ParamFlowClusterConfig clusterConfig = rule.getClusterConfig();
return clusterConfig != null && validClusterRuleId(clusterConfig.getFlowId());
}
public static boolean validClusterRuleId(Long id) {
return id != null && id > 0;
}
public static void fillExceptionFlowItems(ParamFlowRule rule) {
if (rule != null) {
if (rule.getParamFlowItemList() == null) {
rule.setParamFlowItemList(new ArrayList<ParamFlowItem>());
}
Map<Object, Integer> itemMap = parseHotItems(rule.getParamFlowItemList());
rule.setParsedHotItems(itemMap);
}
}
static Map<Object, Integer> parseHotItems(List<ParamFlowItem> items) {
Map<Object, Integer> itemMap = new HashMap<Object, Integer>();
if (items == null || items.isEmpty()) {
return itemMap;
}
for (ParamFlowItem item : items) {
// Value should not be null.
Object value;
try {
value = parseItemValue(item.getObject(), item.getClassType());
} catch (Exception ex) {
RecordLog.warn("[ParamFlowRuleUtil] Failed to parse value for item: " + item, ex);
continue;
}
if (item.getCount() == null || item.getCount() < 0 || value == null) {
RecordLog.warn("[ParamFlowRuleUtil] Ignoring invalid exclusion parameter item: " + item);
continue;
}
itemMap.put(value, item.getCount());
}
return itemMap;
}
static Object parseItemValue(String value, String classType) {
if (value == null) {
throw new IllegalArgumentException("Null value");
}
if (StringUtil.isBlank(classType)) {
// If the class type is not provided, then treat it as string.
return value;
}
// Handle primitive type.
if (int.class.toString().equals(classType) || Integer.class.getName().equals(classType)) {
return Integer.parseInt(value);
} else if (boolean.class.toString().equals(classType) || Boolean.class.getName().equals(classType)) {
return Boolean.parseBoolean(value);
} else if (long.class.toString().equals(classType) || Long.class.getName().equals(classType)) {
return Long.parseLong(value);
} else if (double.class.toString().equals(classType) || Double.class.getName().equals(classType)) {
return Double.parseDouble(value);
} else if (float.class.toString().equals(classType) || Float.class.getName().equals(classType)) {
return Float.parseFloat(value);
} else if (byte.class.toString().equals(classType) || Byte.class.getName().equals(classType)) {
return Byte.parseByte(value);
} else if (short.class.toString().equals(classType) || Short.class.getName().equals(classType)) {
return Short.parseShort(value);
} else if (char.class.toString().equals(classType)) {
char[] array = value.toCharArray();
return array.length > 0 ? array[0] : null;
}
return value;
}
private ParamFlowRuleUtil() {}
}

View File

@ -15,11 +15,9 @@
*/
package com.alibaba.csp.sentinel.slots.block.flow.param;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
@ -109,84 +107,4 @@ public class ParamFlowRuleManagerTest {
assertTrue(allRules.contains(ruleC));
assertTrue(allRules.contains(ruleD));
}
@Test
public void testParseHotParamExceptionItemsFailure() {
String valueB = "Sentinel";
Integer valueC = 6;
char valueD = 6;
float valueE = 11.11f;
// Null object will not be parsed.
ParamFlowItem itemA = new ParamFlowItem(null, 1, double.class.getName());
// Hot item with empty class type will be treated as string.
ParamFlowItem itemB = new ParamFlowItem(valueB, 3, null);
ParamFlowItem itemE = new ParamFlowItem(String.valueOf(valueE), 3, "");
// Bad count will not be parsed.
ParamFlowItem itemC = ParamFlowItem.newItem(valueC, -5);
ParamFlowItem itemD = new ParamFlowItem(String.valueOf(valueD), null, char.class.getName());
List<ParamFlowItem> badItems = Arrays.asList(itemA, itemB, itemC, itemD, itemE);
Map<Object, Integer> parsedItems = ParamFlowRuleManager.parseHotItems(badItems);
// Value B and E will be parsed, but ignoring the type.
assertEquals(2, parsedItems.size());
assertEquals(itemB.getCount(), parsedItems.get(valueB));
assertFalse(parsedItems.containsKey(valueE));
assertEquals(itemE.getCount(), parsedItems.get(String.valueOf(valueE)));
}
@Test
public void testParseHotParamExceptionItemsSuccess() {
// Test for empty list.
assertEquals(0, ParamFlowRuleManager.parseHotItems(null).size());
assertEquals(0, ParamFlowRuleManager.parseHotItems(new ArrayList<ParamFlowItem>()).size());
// Test for boxing objects and primitive types.
Double valueA = 1.1d;
String valueB = "Sentinel";
Integer valueC = 6;
char valueD = 'c';
ParamFlowItem itemA = ParamFlowItem.newItem(valueA, 1);
ParamFlowItem itemB = ParamFlowItem.newItem(valueB, 3);
ParamFlowItem itemC = ParamFlowItem.newItem(valueC, 5);
ParamFlowItem itemD = new ParamFlowItem().setObject(String.valueOf(valueD))
.setClassType(char.class.getName())
.setCount(7);
List<ParamFlowItem> items = Arrays.asList(itemA, itemB, itemC, itemD);
Map<Object, Integer> parsedItems = ParamFlowRuleManager.parseHotItems(items);
assertEquals(itemA.getCount(), parsedItems.get(valueA));
assertEquals(itemB.getCount(), parsedItems.get(valueB));
assertEquals(itemC.getCount(), parsedItems.get(valueC));
assertEquals(itemD.getCount(), parsedItems.get(valueD));
}
@Test
public void testCheckValidHotParamRule() {
// Null or empty resource;
ParamFlowRule rule1 = new ParamFlowRule();
ParamFlowRule rule2 = new ParamFlowRule("");
assertFalse(ParamFlowRuleManager.isValidRule(null));
assertFalse(ParamFlowRuleManager.isValidRule(rule1));
assertFalse(ParamFlowRuleManager.isValidRule(rule2));
// Invalid threshold count.
ParamFlowRule rule3 = new ParamFlowRule("abc")
.setCount(-1)
.setParamIdx(1);
assertFalse(ParamFlowRuleManager.isValidRule(rule3));
// Parameter index not set or invalid.
ParamFlowRule rule4 = new ParamFlowRule("abc")
.setCount(1);
ParamFlowRule rule5 = new ParamFlowRule("abc")
.setCount(1)
.setParamIdx(-1);
assertFalse(ParamFlowRuleManager.isValidRule(rule4));
assertFalse(ParamFlowRuleManager.isValidRule(rule5));
ParamFlowRule goodRule = new ParamFlowRule("abc")
.setCount(10)
.setParamIdx(1);
assertTrue(ParamFlowRuleManager.isValidRule(goodRule));
}
}

View File

@ -0,0 +1,95 @@
package com.alibaba.csp.sentinel.slots.block.flow.param;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* @author Eric Zhao
*/
public class ParamFlowRuleUtilTest {
@Test
public void testCheckValidHotParamRule() {
// Null or empty resource;
ParamFlowRule rule1 = new ParamFlowRule();
ParamFlowRule rule2 = new ParamFlowRule("");
assertFalse(ParamFlowRuleUtil.isValidRule(null));
assertFalse(ParamFlowRuleUtil.isValidRule(rule1));
assertFalse(ParamFlowRuleUtil.isValidRule(rule2));
// Invalid threshold count.
ParamFlowRule rule3 = new ParamFlowRule("abc")
.setCount(-1)
.setParamIdx(1);
assertFalse(ParamFlowRuleUtil.isValidRule(rule3));
// Parameter index not set or invalid.
ParamFlowRule rule4 = new ParamFlowRule("abc")
.setCount(1);
ParamFlowRule rule5 = new ParamFlowRule("abc")
.setCount(1)
.setParamIdx(-1);
assertFalse(ParamFlowRuleUtil.isValidRule(rule4));
assertFalse(ParamFlowRuleUtil.isValidRule(rule5));
ParamFlowRule goodRule = new ParamFlowRule("abc")
.setCount(10)
.setParamIdx(1);
assertTrue(ParamFlowRuleUtil.isValidRule(goodRule));
}
@Test
public void testParseHotParamExceptionItemsFailure() {
String valueB = "Sentinel";
Integer valueC = 6;
char valueD = 6;
float valueE = 11.11f;
// Null object will not be parsed.
ParamFlowItem itemA = new ParamFlowItem(null, 1, double.class.getName());
// Hot item with empty class type will be treated as string.
ParamFlowItem itemB = new ParamFlowItem(valueB, 3, null);
ParamFlowItem itemE = new ParamFlowItem(String.valueOf(valueE), 3, "");
// Bad count will not be parsed.
ParamFlowItem itemC = ParamFlowItem.newItem(valueC, -5);
ParamFlowItem itemD = new ParamFlowItem(String.valueOf(valueD), null, char.class.getName());
List<ParamFlowItem> badItems = Arrays.asList(itemA, itemB, itemC, itemD, itemE);
Map<Object, Integer> parsedItems = ParamFlowRuleUtil.parseHotItems(badItems);
// Value B and E will be parsed, but ignoring the type.
assertEquals(2, parsedItems.size());
assertEquals(itemB.getCount(), parsedItems.get(valueB));
assertFalse(parsedItems.containsKey(valueE));
assertEquals(itemE.getCount(), parsedItems.get(String.valueOf(valueE)));
}
@Test
public void testParseHotParamExceptionItemsSuccess() {
// Test for empty list.
assertEquals(0, ParamFlowRuleUtil.parseHotItems(null).size());
assertEquals(0, ParamFlowRuleUtil.parseHotItems(new ArrayList<ParamFlowItem>()).size());
// Test for boxing objects and primitive types.
Double valueA = 1.1d;
String valueB = "Sentinel";
Integer valueC = 6;
char valueD = 'c';
ParamFlowItem itemA = ParamFlowItem.newItem(valueA, 1);
ParamFlowItem itemB = ParamFlowItem.newItem(valueB, 3);
ParamFlowItem itemC = ParamFlowItem.newItem(valueC, 5);
ParamFlowItem itemD = new ParamFlowItem().setObject(String.valueOf(valueD))
.setClassType(char.class.getName())
.setCount(7);
List<ParamFlowItem> items = Arrays.asList(itemA, itemB, itemC, itemD);
Map<Object, Integer> parsedItems = ParamFlowRuleUtil.parseHotItems(items);
assertEquals(itemA.getCount(), parsedItems.get(valueA));
assertEquals(itemB.getCount(), parsedItems.get(valueB));
assertEquals(itemC.getCount(), parsedItems.get(valueC));
assertEquals(itemD.getCount(), parsedItems.get(valueD));
}
}