diff --git a/sentinel-adapter/pom.xml b/sentinel-adapter/pom.xml
index c573b8b2..13e516cb 100755
--- a/sentinel-adapter/pom.xml
+++ b/sentinel-adapter/pom.xml
@@ -22,6 +22,7 @@
sentinel-zuul-adapter
sentinel-reactor-adapter
sentinel-spring-webflux-adapter
+ sentinel-api-gateway-adapter-common
@@ -46,6 +47,11 @@
sentinel-reactor-adapter
${project.version}
+
+ com.alibaba.csp
+ sentinel-api-gateway-adapter-common
+ ${project.version}
+
junit
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/README.md b/sentinel-adapter/sentinel-api-gateway-adapter-common/README.md
new file mode 100644
index 00000000..9749b443
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/README.md
@@ -0,0 +1,8 @@
+# Sentinel API Gateway Adapter Common
+
+The `sentinel-api-gateway-adapter-common` module provides common abstraction for
+API gateway flow control:
+
+- `GatewayFlowRule`: flow control rule specific for route or API defined in API gateway.
+This can be automatically converted to `FlowRule` or `ParamFlowRule`.
+- `ApiDefinition`: gateway API definition with a group of predicates
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml b/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml
new file mode 100644
index 00000000..acda0b1d
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml
@@ -0,0 +1,36 @@
+
+
+
+ sentinel-adapter
+ com.alibaba.csp
+ 1.6.0-SNAPSHOT
+
+ 4.0.0
+
+ sentinel-api-gateway-adapter-common
+ jar
+
+
+ 1.7
+ 1.7
+
+
+
+
+ com.alibaba.csp
+ sentinel-core
+
+
+ com.alibaba.csp
+ sentinel-parameter-flow-control
+
+
+
+ junit
+ junit
+ test
+
+
+
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/SentinelGatewayConstants.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/SentinelGatewayConstants.java
new file mode 100644
index 00000000..4a2c6ce3
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/SentinelGatewayConstants.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999-2019 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.adapter.gateway.common;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public final class SentinelGatewayConstants {
+
+ public static final int APP_TYPE_GATEWAY = 1;
+
+ public static final int RESOURCE_MODE_ROUTE_ID = 0;
+ public static final int RESOURCE_MODE_CUSTOM_API_NAME = 1;
+
+ public static final int PARAM_PARSE_STRATEGY_CLIENT_IP = 0;
+ public static final int PARAM_PARSE_STRATEGY_HOST = 1;
+ public static final int PARAM_PARSE_STRATEGY_HEADER = 2;
+ public static final int PARAM_PARSE_STRATEGY_URL_PARAM = 3;
+
+ public static final int PARAM_MATCH_STRATEGY_EXACT = 0;
+ public static final int PARAM_MATCH_STRATEGY_PREFIX = 1;
+ public static final int PARAM_MATCH_STRATEGY_REGEX = 2;
+
+ public static final String GATEWAY_CONTEXT_DEFAULT = "sentinel_gateway_context_default";
+ public static final String GATEWAY_CONTEXT_PREFIX = "sentinel_gateway_context$$";
+ public static final String GATEWAY_CONTEXT_ROUTE_PREFIX = "sentinel_gateway_context$$route$$";
+
+ public static final String GATEWAY_NOT_MATCH_PARAM = "$$not_match";
+
+ private SentinelGatewayConstants() {}
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinition.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinition.java
new file mode 100644
index 00000000..6f3a9782
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinition.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 1999-2019 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.adapter.gateway.common.api;
+
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public class ApiDefinition {
+
+ private String apiName;
+ private Set predicateItems;
+
+ public ApiDefinition() {}
+
+ public ApiDefinition(String apiName) {
+ this.apiName = apiName;
+ }
+
+ public String getApiName() {
+ return apiName;
+ }
+
+ public ApiDefinition setApiName(String apiName) {
+ this.apiName = apiName;
+ return this;
+ }
+
+ public Set getPredicateItems() {
+ return predicateItems;
+ }
+
+ public ApiDefinition setPredicateItems(Set predicateItems) {
+ this.predicateItems = predicateItems;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) { return true; }
+ if (o == null || getClass() != o.getClass()) { return false; }
+
+ ApiDefinition that = (ApiDefinition)o;
+
+ if (!Objects.equals(apiName, that.apiName)) { return false; }
+ return Objects.equals(predicateItems, that.predicateItems);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = apiName != null ? apiName.hashCode() : 0;
+ result = 31 * result + (predicateItems != null ? predicateItems.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ApiDefinition{" +
+ "apiName='" + apiName + '\'' +
+ ", predicateItems=" + predicateItems +
+ '}';
+ }
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinitionChangeObserver.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinitionChangeObserver.java
new file mode 100644
index 00000000..5cc9cdc3
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinitionChangeObserver.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1999-2019 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
+ *
+ * https://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.adapter.gateway.common.api;
+
+import java.util.Set;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public interface ApiDefinitionChangeObserver {
+
+ /**
+ * Notify the observer about the new gateway API definitions.
+ *
+ * @param apiDefinitions new set of gateway API definition
+ */
+ void onChange(Set apiDefinitions);
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiPathPredicateItem.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiPathPredicateItem.java
new file mode 100644
index 00000000..1cbce589
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiPathPredicateItem.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 1999-2019 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.adapter.gateway.common.api;
+
+import java.util.Objects;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public class ApiPathPredicateItem implements ApiPredicateItem {
+
+ private String pattern;
+ private int matchStrategy;
+
+ public ApiPathPredicateItem setPattern(String pattern) {
+ this.pattern = pattern;
+ return this;
+ }
+
+ public ApiPathPredicateItem setMatchStrategy(int matchStrategy) {
+ this.matchStrategy = matchStrategy;
+ return this;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public int getMatchStrategy() {
+ return matchStrategy;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) { return true; }
+ if (o == null || getClass() != o.getClass()) { return false; }
+
+ ApiPathPredicateItem that = (ApiPathPredicateItem)o;
+
+ if (matchStrategy != that.matchStrategy) { return false; }
+ return Objects.equals(pattern, that.pattern);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = pattern != null ? pattern.hashCode() : 0;
+ result = 31 * result + matchStrategy;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ApiPathPredicateItem{" +
+ "pattern='" + pattern + '\'' +
+ ", matchStrategy=" + matchStrategy +
+ '}';
+ }
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiPredicateGroupItem.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiPredicateGroupItem.java
new file mode 100644
index 00000000..c45322c2
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiPredicateGroupItem.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2019 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
+ *
+ * https://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.adapter.gateway.common.api;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.alibaba.csp.sentinel.util.AssertUtil;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public class ApiPredicateGroupItem implements ApiPredicateItem {
+
+ private final Set items = new HashSet<>();
+
+ public ApiPredicateGroupItem addItem(ApiPredicateItem item) {
+ AssertUtil.notNull(item, "item cannot be null");
+ items.add(item);
+ return this;
+ }
+
+ public Set getItems() {
+ return items;
+ }
+
+ /*@Override
+ public ApiPredicateItem and(ApiPredicateItem item) {
+ AssertUtil.notNull(item, "item cannot be null");
+ return this.addItem(item);
+ }*/
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiPredicateItem.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiPredicateItem.java
new file mode 100644
index 00000000..d42bc5d6
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiPredicateItem.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2019 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.adapter.gateway.common.api;
+
+import com.alibaba.csp.sentinel.util.AssertUtil;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public interface ApiPredicateItem {
+
+ /**
+ * Combine two {@link ApiPredicateItem}.
+ *
+ * @param item another predicate item
+ * @return combined predicate group item
+ */
+ /*default ApiPredicateItem and(ApiPredicateItem item) {
+ AssertUtil.notNull(item, "item cannot be null");
+ return new ApiPredicateGroupItem()
+ .addItem(this).addItem(item);
+ }*/
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java
new file mode 100644
index 00000000..3985e7d9
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 1999-2019 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
+ *
+ * https://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.adapter.gateway.common.api;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
+import com.alibaba.csp.sentinel.property.PropertyListener;
+import com.alibaba.csp.sentinel.property.SentinelProperty;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.SpiLoader;
+import com.alibaba.csp.sentinel.util.StringUtil;
+
+/**
+ * Manager for gateway API definitions.
+ *
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public final class GatewayApiDefinitionManager {
+
+ private static final Map API_MAP = new ConcurrentHashMap<>();
+
+ private static final ApiDefinitionPropertyListener LISTENER = new ApiDefinitionPropertyListener();
+ private static SentinelProperty> currentProperty = new DynamicSentinelProperty<>();
+
+ /**
+ * The map keeps all found ApiDefinitionChangeObserver (class name as key).
+ */
+ private static final Map API_CHANGE_OBSERVERS = new ConcurrentHashMap<>();
+
+ static {
+ try {
+ currentProperty.addListener(LISTENER);
+ initializeApiChangeObserverSpi();
+ } catch (Throwable ex) {
+ RecordLog.warn("[GatewayApiDefinitionManager] Failed to initialize", ex);
+ ex.printStackTrace();
+ }
+ }
+
+ private static void initializeApiChangeObserverSpi() {
+ List listeners = SpiLoader.loadInstanceList(ApiDefinitionChangeObserver.class);
+ for (ApiDefinitionChangeObserver e : listeners) {
+ API_CHANGE_OBSERVERS.put(e.getClass().getCanonicalName(), e);
+ RecordLog.info("[GatewayApiDefinitionManager] ApiDefinitionChangeObserver added: {0}",
+ e.getClass().getCanonicalName());
+ }
+ }
+
+ public static void register2Property(SentinelProperty> property) {
+ AssertUtil.notNull(property, "property cannot be null");
+ synchronized (LISTENER) {
+ RecordLog.info("[GatewayApiDefinitionManager] Registering new property to gateway API definition manager");
+ currentProperty.removeListener(LISTENER);
+ property.addListener(LISTENER);
+ currentProperty = property;
+ }
+ }
+
+ /**
+ * Load given gateway API definitions and apply to downstream observers.
+ *
+ * @param apiDefinitions set of gateway API definitions
+ * @return true if updated, or else false
+ */
+ public static boolean loadApiDefinitions(Set apiDefinitions) {
+ return currentProperty.updateValue(apiDefinitions);
+ }
+
+ public static ApiDefinition getApiDefinition(final String apiName) {
+ if (apiName == null) {
+ return null;
+ }
+ return API_MAP.get(apiName);
+ }
+
+ public static Set getApiDefinitions() {
+ return new HashSet<>(API_MAP.values());
+ }
+
+ private static final class ApiDefinitionPropertyListener implements PropertyListener> {
+
+ @Override
+ public void configUpdate(Set set) {
+ applyApiUpdateInternal(set);
+ RecordLog.info("[GatewayApiDefinitionManager] Api definition updated: " + API_MAP);
+ }
+
+ @Override
+ public void configLoad(Set set) {
+ applyApiUpdateInternal(set);
+ RecordLog.info("[GatewayApiDefinitionManager] Api definition loaded: " + API_MAP);
+ }
+
+ private static synchronized void applyApiUpdateInternal(Set set) {
+ if (set == null || set.isEmpty()) {
+ API_MAP.clear();
+ notifyDownstreamListeners(new HashSet());
+ return;
+ }
+ Map map = new HashMap<>(set.size());
+ Set validSet = new HashSet<>();
+ for (ApiDefinition definition : set) {
+ if (isValidApi(definition)) {
+ map.put(definition.getApiName(), definition);
+ validSet.add(definition);
+ }
+ }
+
+ API_MAP.clear();
+ API_MAP.putAll(map);
+
+ // propagate to downstream.
+ notifyDownstreamListeners(validSet);
+ }
+ }
+
+ private static void notifyDownstreamListeners(/*@Valid*/ final Set definitions) {
+ try {
+ for (Map.Entry, ApiDefinitionChangeObserver> entry : API_CHANGE_OBSERVERS.entrySet()) {
+ entry.getValue().onChange(definitions);
+ }
+ } catch (Exception ex) {
+ RecordLog.warn("[GatewayApiDefinitionManager] WARN: failed to notify downstream api listeners", ex);
+ }
+ }
+
+ public static boolean isValidApi(ApiDefinition apiDefinition) {
+ return apiDefinition != null && StringUtil.isNotBlank(apiDefinition.getApiName())
+ && apiDefinition.getPredicateItems() != null;
+ }
+
+ static void addApiChangeListener(ApiDefinitionChangeObserver listener) {
+ AssertUtil.notNull(listener, "listener cannot be null");
+ API_CHANGE_OBSERVERS.put(listener.getClass().getCanonicalName(), listener);
+ }
+
+ static void removeApiChangeListener(Class> clazz) {
+ AssertUtil.notNull(clazz, "class cannot be null");
+ API_CHANGE_OBSERVERS.remove(clazz.getCanonicalName());
+ }
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/matcher/AbstractApiMatcher.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/matcher/AbstractApiMatcher.java
new file mode 100644
index 00000000..7fa081ed
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/matcher/AbstractApiMatcher.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1999-2019 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
+ *
+ * https://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.adapter.gateway.common.api.matcher;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.function.Predicate;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public abstract class AbstractApiMatcher implements Predicate {
+
+ protected final String apiName;
+ protected final ApiDefinition apiDefinition;
+ /**
+ * We use {@link com.alibaba.csp.sentinel.util.function.Predicate} here as the min JDK version is 1.7.
+ */
+ protected final Set> matchers = new HashSet<>();
+
+ public AbstractApiMatcher(ApiDefinition apiDefinition) {
+ AssertUtil.notNull(apiDefinition, "apiDefinition cannot be null");
+ AssertUtil.assertNotBlank(apiDefinition.getApiName(), "apiName cannot be empty");
+ this.apiName = apiDefinition.getApiName();
+ this.apiDefinition = apiDefinition;
+
+ try {
+ initializeMatchers();
+ } catch (Exception ex) {
+ RecordLog.warn("[GatewayApiMatcher] Failed to initialize internal matchers", ex);
+ }
+ }
+
+ /**
+ * Initialize the matchers.
+ */
+ protected abstract void initializeMatchers();
+
+ @Override
+ public boolean test(T t) {
+ for (Predicate matcher : matchers) {
+ if (matcher.test(t)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String getApiName() {
+ return apiName;
+ }
+
+ public ApiDefinition getApiDefinition() {
+ return apiDefinition;
+ }
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParser.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParser.java
new file mode 100644
index 00000000..ee3dfaaf
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParser.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 1999-2019 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
+ *
+ * https://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.adapter.gateway.common.param;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
+import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
+import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem;
+import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.util.function.Predicate;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public class GatewayParamParser {
+
+ private final RequestItemParser requestItemParser;
+
+ public GatewayParamParser(RequestItemParser requestItemParser) {
+ AssertUtil.notNull(requestItemParser, "requestItemParser cannot be null");
+ this.requestItemParser = requestItemParser;
+ }
+
+ /**
+ * Parse parameters for given resource from the request entity on condition of the rule predicate.
+ *
+ * @param resource valid resource name
+ * @param request valid request
+ * @param rulePredicate rule predicate indicating the rules to refer
+ * @return the parameter array
+ */
+ public Object[] parseParameterFor(String resource, T request, Predicate rulePredicate) {
+ if (StringUtil.isEmpty(resource) || request == null || rulePredicate == null) {
+ return new Object[0];
+ }
+ Set gatewayRules = new HashSet<>();
+ Set predSet = new HashSet<>();
+ for (GatewayFlowRule rule : GatewayRuleManager.getRulesForResource(resource)) {
+ if (rule.getParamItem() != null) {
+ gatewayRules.add(rule);
+ predSet.add(rulePredicate.test(rule));
+ }
+ }
+ if (gatewayRules.isEmpty()) {
+ return new Object[0];
+ }
+ if (predSet.size() != 1 || predSet.contains(false)) {
+ return new Object[0];
+ }
+ Object[] arr = new Object[gatewayRules.size()];
+ for (GatewayFlowRule rule : gatewayRules) {
+ GatewayParamFlowItem paramItem = rule.getParamItem();
+ int idx = paramItem.getIndex();
+ String param = parseInternal(paramItem, request);
+ arr[idx] = param;
+ }
+ return arr;
+ }
+
+ private String parseInternal(GatewayParamFlowItem item, T request) {
+ switch (item.getParseStrategy()) {
+ case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP:
+ return parseClientIp(item, request);
+ case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HOST:
+ return parseHost(item, request);
+ case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER:
+ return parseHeader(item, request);
+ case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM:
+ return parseUrlParameter(item, request);
+ default:
+ return null;
+ }
+ }
+
+ private String parseClientIp(/*@Valid*/ GatewayParamFlowItem item, T request) {
+ String clientIp = requestItemParser.getRemoteAddress(request);
+ String pattern = item.getPattern();
+ if (pattern == null) {
+ return clientIp;
+ }
+ return parseWithMatchStrategyInternal(item.getMatchStrategy(), clientIp, pattern);
+ }
+
+ private String parseHeader(/*@Valid*/ GatewayParamFlowItem item, T request) {
+ String headerKey = item.getFieldName();
+ String pattern = item.getPattern();
+ // TODO: what if the header has multiple values?
+ String headerValue = requestItemParser.getHeader(request, headerKey);
+ if (pattern == null) {
+ return headerValue;
+ }
+ // Match value according to regex pattern or exact mode.
+ return parseWithMatchStrategyInternal(item.getMatchStrategy(), headerValue, pattern);
+ }
+
+ private String parseHost(/*@Valid*/ GatewayParamFlowItem item, T request) {
+ String pattern = item.getPattern();
+ String host = requestItemParser.getHeader(request, "Host");
+ if (pattern == null) {
+ return host;
+ }
+ // Match value according to regex pattern or exact mode.
+ return parseWithMatchStrategyInternal(item.getMatchStrategy(), host, pattern);
+ }
+
+ private String parseUrlParameter(/*@Valid*/ GatewayParamFlowItem item, T request) {
+ String paramName = item.getFieldName();
+ String pattern = item.getPattern();
+ String param = requestItemParser.getUrlParam(request, paramName);
+ if (pattern == null) {
+ return param;
+ }
+ // Match value according to regex pattern or exact mode.
+ return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern);
+ }
+
+ private String parseWithMatchStrategyInternal(int matchStrategy, String value, String pattern) {
+ // TODO: implement here.
+ if (value == null) {
+ return null;
+ }
+ if (matchStrategy == SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX) {
+ return value;
+ }
+ return value;
+ }
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/RequestItemParser.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/RequestItemParser.java
new file mode 100644
index 00000000..5b28abe7
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/RequestItemParser.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1999-2019 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
+ *
+ * https://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.adapter.gateway.common.param;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public interface RequestItemParser {
+
+ /**
+ * Get API path from the request.
+ *
+ * @param request valid request
+ * @return API path
+ */
+ String getPath(T request);
+
+ /**
+ * Get remote address from the request.
+ *
+ * @param request valid request
+ * @return remote address
+ */
+ String getRemoteAddress(T request);
+
+ /**
+ * Get the header associated with the header key.
+ *
+ * @param request valid request
+ * @param key valid header key
+ * @return the header
+ */
+ String getHeader(T request, String key);
+
+ /**
+ * Get the parameter value associated with the parameter name.
+ *
+ * @param request valid request
+ * @param paramName valid parameter name
+ * @return the parameter value
+ */
+ String getUrlParam(T request, String paramName);
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayFlowRule.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayFlowRule.java
new file mode 100644
index 00000000..0486d48a
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayFlowRule.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 1999-2019 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
+ *
+ * https://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.adapter.gateway.common.rule;
+
+import java.util.Objects;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public class GatewayFlowRule {
+
+ private String resource;
+ private int resourceMode = SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID;
+
+ private int grade = RuleConstant.FLOW_GRADE_QPS;
+ private double count;
+ private long intervalSec = 1;
+
+ private int controlBehavior = RuleConstant.CONTROL_BEHAVIOR_DEFAULT;
+ private int burst;
+ /**
+ * For throttle (rate limiting with queueing).
+ */
+ private int maxQueueingTimeoutMs = 500;
+
+ /**
+ * For parameter flow control. If not set, the gateway rule will be
+ * converted to normal flow rule.
+ */
+ private GatewayParamFlowItem paramItem;
+
+ public GatewayFlowRule() {}
+
+ public GatewayFlowRule(String resource) {
+ this.resource = resource;
+ }
+
+ public String getResource() {
+ return resource;
+ }
+
+ public GatewayFlowRule setResource(String resource) {
+ this.resource = resource;
+ return this;
+ }
+
+ public int getResourceMode() {
+ return resourceMode;
+ }
+
+ public GatewayFlowRule setResourceMode(int resourceMode) {
+ this.resourceMode = resourceMode;
+ return this;
+ }
+
+ public int getGrade() {
+ return grade;
+ }
+
+ public GatewayFlowRule setGrade(int grade) {
+ this.grade = grade;
+ return this;
+ }
+
+ public int getControlBehavior() {
+ return controlBehavior;
+ }
+
+ public GatewayFlowRule setControlBehavior(int controlBehavior) {
+ this.controlBehavior = controlBehavior;
+ return this;
+ }
+
+ public double getCount() {
+ return count;
+ }
+
+ public GatewayFlowRule setCount(double count) {
+ this.count = count;
+ return this;
+ }
+
+ public long getIntervalSec() {
+ return intervalSec;
+ }
+
+ public GatewayFlowRule setIntervalSec(long intervalSec) {
+ this.intervalSec = intervalSec;
+ return this;
+ }
+
+ public int getBurst() {
+ return burst;
+ }
+
+ public GatewayFlowRule setBurst(int burst) {
+ this.burst = burst;
+ return this;
+ }
+
+ public GatewayParamFlowItem getParamItem() {
+ return paramItem;
+ }
+
+ public GatewayFlowRule setParamItem(GatewayParamFlowItem paramItem) {
+ this.paramItem = paramItem;
+ return this;
+ }
+
+ public int getMaxQueueingTimeoutMs() {
+ return maxQueueingTimeoutMs;
+ }
+
+ public GatewayFlowRule setMaxQueueingTimeoutMs(int maxQueueingTimeoutMs) {
+ this.maxQueueingTimeoutMs = maxQueueingTimeoutMs;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) { return true; }
+ if (o == null || getClass() != o.getClass()) { return false; }
+
+ GatewayFlowRule rule = (GatewayFlowRule)o;
+
+ if (resourceMode != rule.resourceMode) { return false; }
+ if (grade != rule.grade) { return false; }
+ if (Double.compare(rule.count, count) != 0) { return false; }
+ if (intervalSec != rule.intervalSec) { return false; }
+ if (controlBehavior != rule.controlBehavior) { return false; }
+ if (burst != rule.burst) { return false; }
+ if (maxQueueingTimeoutMs != rule.maxQueueingTimeoutMs) { return false; }
+ if (!Objects.equals(resource, rule.resource)) { return false; }
+ return Objects.equals(paramItem, rule.paramItem);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result;
+ long temp;
+ result = resource != null ? resource.hashCode() : 0;
+ result = 31 * result + resourceMode;
+ result = 31 * result + grade;
+ temp = Double.doubleToLongBits(count);
+ result = 31 * result + (int)(temp ^ (temp >>> 32));
+ result = 31 * result + (int)(intervalSec ^ (intervalSec >>> 32));
+ result = 31 * result + controlBehavior;
+ result = 31 * result + burst;
+ result = 31 * result + maxQueueingTimeoutMs;
+ result = 31 * result + (paramItem != null ? paramItem.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "GatewayFlowRule{" +
+ "resource='" + resource + '\'' +
+ ", resourceMode=" + resourceMode +
+ ", grade=" + grade +
+ ", count=" + count +
+ ", intervalSec=" + intervalSec +
+ ", controlBehavior=" + controlBehavior +
+ ", burst=" + burst +
+ ", maxQueueingTimeoutMs=" + maxQueueingTimeoutMs +
+ ", paramItem=" + paramItem +
+ '}';
+ }
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayParamFlowItem.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayParamFlowItem.java
new file mode 100644
index 00000000..11e4a908
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayParamFlowItem.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 1999-2019 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
+ *
+ * https://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.adapter.gateway.common.rule;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public class GatewayParamFlowItem {
+
+ /**
+ * Should be set when applying to parameter flow rules.
+ */
+ private Integer index;
+
+ /**
+ * Strategy for parsing item (e.g. client IP, arbitrary headers and URL parameters).
+ */
+ private int parseStrategy;
+ /**
+ * Field to get (only required for arbitrary headers or URL parameters mode).
+ */
+ private String fieldName;
+ /**
+ * Matching pattern. If not set, all values will be kept in LRU map.
+ */
+ private String pattern;
+ /**
+ * Matching strategy for item value.
+ */
+ private int matchStrategy = SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT;
+
+ public Integer getIndex() {
+ return index;
+ }
+
+ GatewayParamFlowItem setIndex(Integer index) {
+ this.index = index;
+ return this;
+ }
+
+ public int getParseStrategy() {
+ return parseStrategy;
+ }
+
+ public GatewayParamFlowItem setParseStrategy(int parseStrategy) {
+ this.parseStrategy = parseStrategy;
+ return this;
+ }
+
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ public GatewayParamFlowItem setFieldName(String fieldName) {
+ this.fieldName = fieldName;
+ return this;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public GatewayParamFlowItem setPattern(String pattern) {
+ this.pattern = pattern;
+ return this;
+ }
+
+ public int getMatchStrategy() {
+ return matchStrategy;
+ }
+
+ public GatewayParamFlowItem setMatchStrategy(int matchStrategy) {
+ this.matchStrategy = matchStrategy;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "GatewayParamFlowItem{" +
+ "index=" + index +
+ ", parseStrategy=" + parseStrategy +
+ ", fieldName='" + fieldName + '\'' +
+ ", pattern='" + pattern + '\'' +
+ ", matchStrategy=" + matchStrategy +
+ '}';
+ }
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleConverter.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleConverter.java
new file mode 100644
index 00000000..e2b091ba
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleConverter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2019 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
+ *
+ * https://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.adapter.gateway.common.rule;
+
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+final class GatewayRuleConverter {
+
+ static FlowRule toFlowRule(/*@Valid*/ GatewayFlowRule rule) {
+ return new FlowRule(rule.getResource())
+ .setControlBehavior(rule.getControlBehavior())
+ .setCount(rule.getCount())
+ .setGrade(rule.getGrade())
+ .setMaxQueueingTimeMs(rule.getMaxQueueingTimeoutMs());
+ }
+
+ /**
+ * Convert a gateway rule to parameter flow rule, then apply the generated
+ * parameter index to {@link GatewayParamFlowItem} of the rule.
+ *
+ * @param gatewayRule a valid gateway rule that should contain valid parameter items
+ * @param idx generated parameter index (callers should guarantee it's unique and incremental)
+ * @return converted parameter flow rule
+ */
+ static ParamFlowRule applyToParamRule(/*@Valid*/ GatewayFlowRule gatewayRule, int idx) {
+ ParamFlowRule paramRule = new ParamFlowRule(gatewayRule.getResource())
+ .setCount(gatewayRule.getCount())
+ .setGrade(gatewayRule.getGrade())
+ .setDurationInSec(gatewayRule.getIntervalSec())
+ .setBurstCount(gatewayRule.getBurst())
+ .setControlBehavior(gatewayRule.getControlBehavior())
+ .setMaxQueueingTimeMs(gatewayRule.getMaxQueueingTimeoutMs())
+ .setParamIdx(idx);
+ GatewayParamFlowItem gatewayItem = gatewayRule.getParamItem();
+ // Apply the current idx to gateway rule item.
+ gatewayItem.setIndex(idx);
+ // TODO: implement for pattern-based parameters.
+ return paramRule;
+ }
+
+ private GatewayRuleConverter() {}
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManager.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManager.java
new file mode 100644
index 00000000..f0ca5fd7
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManager.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 1999-2019 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.adapter.gateway.common.rule;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
+import com.alibaba.csp.sentinel.property.PropertyListener;
+import com.alibaba.csp.sentinel.property.SentinelProperty;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.StringUtil;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public final class GatewayRuleManager {
+
+ private static final Map> RULE_MAP = new ConcurrentHashMap<>();
+
+ private static final GatewayRulePropertyListener LISTENER = new GatewayRulePropertyListener();
+ private static SentinelProperty> currentProperty = new DynamicSentinelProperty<>();
+
+ static {
+ currentProperty.addListener(LISTENER);
+ }
+
+ public static void register2Property(SentinelProperty> property) {
+ AssertUtil.notNull(property, "property cannot be null");
+ synchronized (LISTENER) {
+ RecordLog.info("[GatewayRuleManager] Registering new property to gateway flow rule manager");
+ currentProperty.removeListener(LISTENER);
+ property.addListener(LISTENER);
+ currentProperty = property;
+ }
+ }
+
+ /**
+ * Load all provided gateway rules into memory, while
+ * previous rules will be replaced.
+ *
+ * @param rules rule set
+ * @return true if updated, otherwise false
+ */
+ public static boolean loadRules(Set rules) {
+ return currentProperty.updateValue(rules);
+ }
+
+ public static Set getRules() {
+ Set rules = new HashSet<>();
+ for (Set ruleSet : RULE_MAP.values()) {
+ rules.addAll(ruleSet);
+ }
+ return rules;
+ }
+
+ public static Set getRulesForResource(String resourceName) {
+ AssertUtil.assertNotBlank(resourceName, "resourceName cannot be blank");
+ Set set = RULE_MAP.get(resourceName);
+ if (set == null) {
+ return new HashSet<>();
+ }
+ return new HashSet<>(set);
+ }
+
+ private static final class GatewayRulePropertyListener implements PropertyListener> {
+
+ @Override
+ public void configUpdate(Set conf) {
+ applyGatewayRuleInternal(conf);
+ RecordLog.info("[GatewayRuleManager] Gateway flow rules received: " + RULE_MAP);
+ }
+
+ @Override
+ public void configLoad(Set conf) {
+ applyGatewayRuleInternal(conf);
+ RecordLog.info("[GatewayRuleManager] Gateway flow rules loaded: " + RULE_MAP);
+ }
+
+ private synchronized void applyGatewayRuleInternal(Set conf) {
+ if (conf == null || conf.isEmpty()) {
+ FlowRuleManager.loadRules(new ArrayList());
+ ParamFlowRuleManager.loadRules(new ArrayList());
+ RULE_MAP.clear();
+ return;
+ }
+ Map> gatewayRuleMap = new ConcurrentHashMap<>();
+ Map idxMap = new HashMap<>();
+ List flowRules = new ArrayList<>();
+ Set paramFlowRules = new HashSet<>();
+
+ for (GatewayFlowRule rule : conf) {
+ if (!isValidRule(rule)) {
+ RecordLog.warn("[GatewayRuleManager] Ignoring invalid rule when loading new rules: " + rule);
+ continue;
+ }
+ String resourceName = rule.getResource();
+ if (rule.getParamItem() == null) {
+ // If param item is absent, it will be converted to normal flow rule.
+ flowRules.add(GatewayRuleConverter.toFlowRule(rule));
+ } else {
+ // Prepare index map.
+ if (!idxMap.containsKey(resourceName)) {
+ idxMap.put(resourceName, 0);
+ }
+ int idx = idxMap.get(resourceName);
+ // Convert to parameter flow rule.
+ if (paramFlowRules.add(GatewayRuleConverter.applyToParamRule(rule, idx))) {
+ idxMap.put(rule.getResource(), idx + 1);
+ }
+ }
+ // Apply to the gateway rule map.
+ Set ruleSet = gatewayRuleMap.get(resourceName);
+ if (ruleSet == null) {
+ ruleSet = new HashSet<>();
+ gatewayRuleMap.put(resourceName, ruleSet);
+ }
+ ruleSet.add(rule);
+ }
+ FlowRuleManager.loadRules(flowRules);
+ ParamFlowRuleManager.loadRules(new ArrayList<>(paramFlowRules));
+
+ RULE_MAP.clear();
+ RULE_MAP.putAll(gatewayRuleMap);
+ }
+ }
+
+ public static boolean isValidRule(GatewayFlowRule rule) {
+ if (rule == null || StringUtil.isBlank(rule.getResource()) || rule.getResourceMode() < 0
+ || rule.getGrade() < 0 || rule.getCount() < 0 || rule.getBurst() < 0 || rule.getControlBehavior() < 0) {
+ return false;
+ }
+ if (rule.getGrade() == RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
+ && rule.getMaxQueueingTimeoutMs() < 0) {
+ return false;
+ }
+ if (rule.getIntervalSec() <= 0) {
+ return false;
+ }
+ GatewayParamFlowItem item = rule.getParamItem();
+ if (item != null) {
+ return isValidParamItem(item);
+ }
+ return true;
+ }
+
+ static boolean isValidParamItem(/*@NonNull*/ GatewayParamFlowItem item) {
+ if (item.getParseStrategy() < 0) {
+ return false;
+ }
+ if (item.getParseStrategy() == SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM ||
+ item.getParseStrategy() == SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER) {
+ if (StringUtil.isBlank(item.getFieldName())) {
+ return false;
+ }
+ }
+ return StringUtil.isEmpty(item.getPattern()) || item.getMatchStrategy() >= 0;
+ }
+
+ private GatewayRuleManager() {}
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManagerTest.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManagerTest.java
new file mode 100644
index 00000000..0eed160e
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManagerTest.java
@@ -0,0 +1,27 @@
+package com.alibaba.csp.sentinel.adapter.gateway.common.api;
+
+import java.util.Collections;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Eric Zhao
+ */
+public class GatewayApiDefinitionManagerTest {
+
+ @Test
+ public void testIsValidApi() {
+ ApiDefinition bad1 = new ApiDefinition();
+ ApiDefinition bad2 = new ApiDefinition("foo");
+ ApiDefinition good1 = new ApiDefinition("foo")
+ .setPredicateItems(Collections.singleton(new ApiPathPredicateItem()
+ .setPattern("/abc")
+ ));
+
+ assertFalse(GatewayApiDefinitionManager.isValidApi(bad1));
+ assertFalse(GatewayApiDefinitionManager.isValidApi(bad2));
+ assertTrue(GatewayApiDefinitionManager.isValidApi(good1));
+ }
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleConverterTest.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleConverterTest.java
new file mode 100644
index 00000000..0a9431a1
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleConverterTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2019 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
+ *
+ * https://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.adapter.gateway.common.rule;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Eric Zhao
+ */
+public class GatewayRuleConverterTest {
+
+ @Test
+ public void testConvertToFlowRule() {
+ GatewayFlowRule rule = new GatewayFlowRule("routeId1")
+ .setCount(10)
+ .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
+ .setMaxQueueingTimeoutMs(1000);
+ FlowRule flowRule = GatewayRuleConverter.toFlowRule(rule);
+ assertEquals(rule.getResource(), flowRule.getResource());
+ assertEquals(rule.getCount(), flowRule.getCount(), 0.01);
+ assertEquals(rule.getControlBehavior(), flowRule.getControlBehavior());
+ assertEquals(rule.getMaxQueueingTimeoutMs(), flowRule.getMaxQueueingTimeMs());
+ }
+
+ @Test
+ public void testConvertAndApplyToParamRule() {
+ GatewayFlowRule routeRule1 = new GatewayFlowRule("routeId1")
+ .setCount(2)
+ .setIntervalSec(2)
+ .setBurst(2)
+ .setParamItem(new GatewayParamFlowItem()
+ .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
+ );
+ int idx = 1;
+ ParamFlowRule paramRule = GatewayRuleConverter.applyToParamRule(routeRule1, idx);
+ assertEquals(routeRule1.getResource(), paramRule.getResource());
+ assertEquals(routeRule1.getCount(), paramRule.getCount(), 0.01);
+ assertEquals(routeRule1.getControlBehavior(), paramRule.getControlBehavior());
+ assertEquals(routeRule1.getIntervalSec(), paramRule.getDurationInSec());
+ assertEquals(routeRule1.getBurst(), paramRule.getBurstCount());
+ assertEquals(idx, (int)paramRule.getParamIdx());
+ assertEquals(idx, (int)routeRule1.getParamItem().getIndex());
+ }
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManagerTest.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManagerTest.java
new file mode 100644
index 00000000..ab1fcc15
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManagerTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 1999-2019 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
+ *
+ * https://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.adapter.gateway.common.rule;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Eric Zhao
+ */
+public class GatewayRuleManagerTest {
+
+ @Test
+ public void testLoadAndGetGatewayRules() {
+ Set rules = new HashSet<>();
+ String ahasRoute = "ahas_route";
+ GatewayFlowRule rule1 = new GatewayFlowRule(ahasRoute)
+ .setCount(500)
+ .setIntervalSec(1);
+ GatewayFlowRule rule2 = new GatewayFlowRule(ahasRoute)
+ .setCount(20)
+ .setIntervalSec(2)
+ .setBurst(5)
+ .setParamItem(new GatewayParamFlowItem()
+ .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
+ );
+ GatewayFlowRule rule3 = new GatewayFlowRule("complex_route_ZZZ")
+ .setCount(10)
+ .setIntervalSec(1)
+ .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
+ .setMaxQueueingTimeoutMs(600)
+ .setParamItem(new GatewayParamFlowItem()
+ .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
+ .setFieldName("X-Sentinel-Flag")
+ );
+ rules.add(rule1);
+ rules.add(rule2);
+ rules.add(rule3);
+ GatewayRuleManager.loadRules(rules);
+
+ assertTrue(FlowRuleManager.hasConfig(ahasRoute));
+ assertTrue(ParamFlowRuleManager.hasRules(ahasRoute));
+ assertEquals(0, (int)rule2.getParamItem().getIndex());
+ assertEquals(0, (int)rule3.getParamItem().getIndex());
+ assertTrue(GatewayRuleManager.getRulesForResource(ahasRoute).contains(rule1));
+ assertTrue(GatewayRuleManager.getRulesForResource(ahasRoute).contains(rule2));
+ }
+
+ @Test
+ public void testIsValidRule() {
+ GatewayFlowRule bad1 = new GatewayFlowRule();
+ GatewayFlowRule bad2 = new GatewayFlowRule("abc")
+ .setIntervalSec(0);
+ GatewayFlowRule bad3 = new GatewayFlowRule("abc")
+ .setParamItem(new GatewayParamFlowItem()
+ .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM));
+ GatewayFlowRule bad4 = new GatewayFlowRule("abc")
+ .setParamItem(new GatewayParamFlowItem()
+ .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
+ .setFieldName("p")
+ .setPattern("def")
+ .setMatchStrategy(-1)
+ );
+ GatewayFlowRule good1 = new GatewayFlowRule("abc");
+ GatewayFlowRule good2 = new GatewayFlowRule("abc")
+ .setParamItem(new GatewayParamFlowItem().setParseStrategy(0));
+ GatewayFlowRule good3 = new GatewayFlowRule("abc")
+ .setParamItem(new GatewayParamFlowItem()
+ .setMatchStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
+ .setFieldName("Origin")
+ .setPattern("def"));
+ assertFalse(GatewayRuleManager.isValidRule(bad1));
+ assertFalse(GatewayRuleManager.isValidRule(bad2));
+ assertFalse(GatewayRuleManager.isValidRule(bad3));
+ assertFalse(GatewayRuleManager.isValidRule(bad4));
+
+ assertTrue(GatewayRuleManager.isValidRule(good1));
+ assertTrue(GatewayRuleManager.isValidRule(good2));
+ assertTrue(GatewayRuleManager.isValidRule(good3));
+ }
+
+ @Before
+ public void setUp() {
+ GatewayRuleManager.loadRules(new HashSet());
+ }
+
+ @After
+ public void tearDown() {
+ GatewayRuleManager.loadRules(new HashSet());
+ }
+}
\ No newline at end of file