getExceptionClass() {
- return Throwable.class;
- }
-
- @Override
- public int getOrder() {
- return 20000;
- }
-
- @Override
- public Response convert(SwaggerInvocation swaggerInvocation, Throwable e) {
- InvocationException invocationException = new InvocationException(Status.INTERNAL_SERVER_ERROR.getStatusCode(), "",
- new CommonExceptionData("Unexpected exception when processing the request."), e);
- return Response.failResp(invocationException);
- }
-}
diff --git a/core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java b/core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java
index 557ad73e16f..f03547c171a 100644
--- a/core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java
+++ b/core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java
@@ -26,7 +26,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
-public class ExecutorManager {
+public final class ExecutorManager {
public static final String KEY_EXECUTORS_PREFIX = "servicecomb.executors.Provider.";
public static final String KEY_EXECUTORS_DEFAULT = "servicecomb.executors.default";
@@ -111,7 +111,7 @@ public Executor findExecutor(OperationMeta operationMeta, Executor defaultOperat
return findExecutorById(EXECUTOR_DEFAULT);
}
- protected Executor findByKey(String configKey) {
+ private Executor findByKey(String configKey) {
String id = environment.getProperty(configKey);
if (id == null) {
return null;
diff --git a/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java b/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java
index 24e41d407cd..13562914fac 100644
--- a/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java
+++ b/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java
@@ -100,7 +100,7 @@ public GroupExecutor init(String groupName) {
return this;
}
- public void initConfig() {
+ public synchronized void initConfig() {
if (LOG_PRINTED.compareAndSet(false, true)) {
LOGGER.info("thread pool rules:\n"
+ "1.use core threads.\n"
diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/AbstractFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/AbstractFilter.java
new file mode 100644
index 00000000000..17d555fc8d1
--- /dev/null
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/AbstractFilter.java
@@ -0,0 +1,63 @@
+/*
+ * 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.servicecomb.core.filter;
+
+import org.springframework.context.EnvironmentAware;
+import org.springframework.core.env.Environment;
+
+public abstract class AbstractFilter implements Filter, EnvironmentAware {
+ private static final String ORDER_KEY = "servicecomb.filter.%s.%s.%s.order";
+
+ private static final String ENABLE_KEY = "servicecomb.filter.%s.%s.%s.enabled";
+
+ protected Environment environment;
+
+ private String nameWithOrder;
+
+ @Override
+ public void setEnvironment(Environment environment) {
+ this.environment = environment;
+ }
+
+ @Override
+ public int getOrder(String application, String serviceName) {
+ Integer custom = environment.getProperty(String.format(ORDER_KEY, getName(), application, serviceName),
+ Integer.class);
+ if (custom != null) {
+ return custom;
+ }
+ return getOrder();
+ }
+
+ @Override
+ public boolean enabledForMicroservice(String application, String serviceName) {
+ Boolean custom = environment.getProperty(String.format(ENABLE_KEY, getName(), application, serviceName),
+ Boolean.class);
+ if (custom != null) {
+ return custom;
+ }
+ return true;
+ }
+
+ @Override
+ public String getNameWithOrder() {
+ if (nameWithOrder == null) {
+ nameWithOrder = String.format("F(%1$06d)-%2$s", getOrder(), getName());
+ }
+ return nameWithOrder;
+ }
+}
diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.java
index bd658be71cf..de13aa0ac2e 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.java
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.java
@@ -16,11 +16,6 @@
*/
package org.apache.servicecomb.core.filter;
-import org.apache.servicecomb.swagger.invocation.InvocationType;
-
public interface ConsumerFilter extends Filter {
- @Override
- default boolean enabledForInvocationType(InvocationType invocationType) {
- return invocationType == InvocationType.CONSUMER;
- }
+
}
diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java b/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java
index 23bc14eac53..0d9ce402624 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java
@@ -16,31 +16,45 @@
*/
package org.apache.servicecomb.core.filter;
+import org.apache.servicecomb.core.filter.impl.ContextMapperFilter;
import org.apache.servicecomb.core.filter.impl.ParameterValidatorFilter;
import org.apache.servicecomb.core.filter.impl.ProviderOperationFilter;
+import org.apache.servicecomb.core.filter.impl.RetryFilter;
import org.apache.servicecomb.core.filter.impl.ScheduleFilter;
+import org.apache.servicecomb.governance.handler.MapperHandler;
+import org.apache.servicecomb.governance.handler.RetryHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CoreFilterConfiguration {
@Bean
- public ProviderFilter producerOperationFilter() {
+ public ProviderOperationFilter scbProducerOperationFilter() {
return new ProviderOperationFilter();
}
@Bean
- public ProviderFilter scheduleFilter() {
+ public ScheduleFilter scbScheduleFilter() {
return new ScheduleFilter();
}
@Bean
- public FilterChainsManager filterChainsManager() {
+ public RetryFilter scbRetryFilter(RetryHandler retryHandler) {
+ return new RetryFilter(retryHandler);
+ }
+
+ @Bean
+ public ContextMapperFilter scbContextMapperFilter(MapperHandler mapperHandler) {
+ return new ContextMapperFilter(mapperHandler);
+ }
+
+ @Bean
+ public FilterChainsManager scbFilterChainsManager() {
return new FilterChainsManager();
}
@Bean
- public ProviderFilter parameterValidatorFilter() {
+ public ParameterValidatorFilter scbParameterValidatorFilter() {
return new ParameterValidatorFilter();
}
}
diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/EdgeFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/EdgeFilter.java
new file mode 100644
index 00000000000..70d8bef4ef5
--- /dev/null
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/EdgeFilter.java
@@ -0,0 +1,21 @@
+/*
+ * 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.servicecomb.core.filter;
+
+public interface EdgeFilter extends Filter {
+
+}
diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/Filter.java b/core/src/main/java/org/apache/servicecomb/core/filter/Filter.java
index 48e4e8ffc32..99ed798e686 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/Filter.java
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/Filter.java
@@ -19,15 +19,15 @@
import java.util.concurrent.CompletableFuture;
import org.apache.servicecomb.core.Invocation;
-import org.apache.servicecomb.swagger.invocation.InvocationType;
import org.apache.servicecomb.swagger.invocation.Response;
+import org.springframework.core.Ordered;
/**
*
* Filters are the basics of how an invocation is executed.
*
* thread rule:
- * assume a producer filter chains is: f1, f2, schedule, f3, f4
+ * assume a provider filter chains is: f1, f2, schedule, f3, f4
*
* schedule is a builtIn filter, which will dispatch invocations to operation related threadPool
*
@@ -47,15 +47,11 @@
* (reactive golden rule)
*
*/
-public interface Filter {
+public interface Filter extends Ordered {
int PROVIDER_SCHEDULE_FILTER_ORDER = 0;
int CONSUMER_LOAD_BALANCE_ORDER = 0;
- default boolean enabledForInvocationType(InvocationType invocationType) {
- return true;
- }
-
default boolean enabledForTransport(String transport) {
return true;
}
@@ -64,7 +60,11 @@ default boolean enabledForMicroservice(String application, String serviceName) {
return true;
}
- default int getOrder(InvocationType invocationType, String application, String serviceName) {
+ default int getOrder(String application, String serviceName) {
+ return 0;
+ }
+
+ default int getOrder() {
return 0;
}
@@ -72,6 +72,8 @@ default String getName() {
throw new IllegalStateException("must provide unique filter name.");
}
+ String getNameWithOrder();
+
/**
*
* @param invocation invocation
diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java b/core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java
index 4f7392f861a..2cc6281d018 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java
@@ -22,26 +22,30 @@
import java.util.List;
import java.util.stream.Collectors;
-import org.apache.servicecomb.swagger.invocation.InvocationType;
import org.springframework.beans.factory.annotation.Autowired;
public class FilterChainsManager {
- private final InvocationFilterChains consumerChains = new InvocationFilterChains(InvocationType.CONSUMER);
+ private InvocationFilterChains consumerChains;
- private final InvocationFilterChains producerChains = new InvocationFilterChains(InvocationType.PROVIDER);
+ private InvocationFilterChains providerChains;
+
+ private InvocationFilterChains edgeChains;
@Autowired
- public FilterChainsManager addFilters(List filters) {
- for (Filter filter : filters) {
- if (filter.enabledForInvocationType(InvocationType.CONSUMER)) {
- consumerChains.addFilter(filter);
- }
+ public FilterChainsManager setEdgeFilters(List filters) {
+ edgeChains = new InvocationFilterChains(filters);
+ return this;
+ }
- if (filter.enabledForInvocationType(InvocationType.PROVIDER)) {
- producerChains.addFilter(filter);
- }
- }
+ @Autowired
+ public FilterChainsManager setConsumerFilters(List filters) {
+ consumerChains = new InvocationFilterChains(filters);
+ return this;
+ }
+ @Autowired
+ public FilterChainsManager setProviderFilters(List filters) {
+ providerChains = new InvocationFilterChains(filters);
return this;
}
@@ -54,24 +58,31 @@ public FilterNode findConsumerChain(String application, String serviceName) {
}
public FilterNode findProducerChain(String application, String serviceName) {
- return producerChains.findChain(application, serviceName);
+ return providerChains.findChain(application, serviceName);
+ }
+
+ public FilterNode findEdgeChain(String application, String serviceName) {
+ return edgeChains.findChain(application, serviceName);
}
public String collectResolvedChains() {
StringBuilder sb = new StringBuilder();
appendLine(sb, "consumer: ");
- appendLine(sb, " filters: %s", collectFilterNames(consumerChains, InvocationType.CONSUMER));
+ appendLine(sb, " filters: %s", collectFilterNames(consumerChains));
appendLine(sb, "producer: ");
- appendLine(sb, " filters: %s", collectFilterNames(producerChains, InvocationType.PROVIDER));
+ appendLine(sb, " filters: %s", collectFilterNames(providerChains));
+
+ appendLine(sb, "edge: ");
+ appendLine(sb, " filters: %s", collectFilterNames(edgeChains));
return deleteLast(sb, 1).toString();
}
- private List collectFilterNames(InvocationFilterChains chains, InvocationType invocationType) {
+ private List collectFilterNames(InvocationFilterChains chains) {
return chains.getFilters().stream()
- .map(filter -> filter.getName() + "(" + filter.getOrder(invocationType, null, null) + ")")
+ .map(filter -> filter.getName() + "(" + filter.getOrder() + ")")
.collect(Collectors.toList());
}
}
diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java b/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java
index b5cb63c0195..44a5b34d901 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java
@@ -62,19 +62,14 @@ private void setNextNode(FilterNode nextNode) {
}
public CompletableFuture onFilter(Invocation invocation) {
- if (!filter.enabledForTransport(invocation.getTransportName())) {
+ // When transport name is empty, maybe edge transport filters need to be executed.
+ // Can't set Endpoint before load balance in edge.
+ if (invocation.getTransportName() != null && !filter.enabledForTransport(invocation.getTransportName())) {
return nextNode.onFilter(invocation);
}
- return AsyncUtils.tryCatchSupplierFuture(() -> filter.onFilter(invocation, nextNode))
- .thenApply(this::rethrowExceptionInResponse);
- }
-
- private Response rethrowExceptionInResponse(Response response) {
- if (response.isFailed() && response.getResult() instanceof Throwable) {
- throw AsyncUtils.rethrow(response.getResult());
- }
-
- return response;
+ String stage = invocation.getInvocationStageTrace().recordStageBegin(this.filter.getNameWithOrder());
+ return AsyncUtils.tryCatchSupplierFuture(() -> filter.onFilter(invocation, nextNode)
+ .whenComplete((r, e) -> invocation.getInvocationStageTrace().recordStageEnd(stage)));
}
}
diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.java b/core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.java
index 064e4471b8a..6dde66c3354 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.java
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.java
@@ -14,47 +14,38 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.servicecomb.core.filter;
-import java.util.Collection;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
-import org.apache.servicecomb.swagger.invocation.InvocationType;
+import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
public class InvocationFilterChains {
- private final Map filters = new HashMap<>();
-
- private final Map microserviceChains = new HashMap<>();
- private final InvocationType invocationType;
+ private final List extends Filter> filters;
- public InvocationFilterChains(InvocationType invocationType) {
- this.invocationType = invocationType;
- }
+ private final Map> microserviceChains = new ConcurrentHashMapEx<>();
- public Collection getFilters() {
- return filters.values();
+ public InvocationFilterChains(List extends Filter> filters) {
+ this.filters = filters;
}
- public void addFilter(Filter filter) {
- filters.put(filter.getName(), filter);
+ public List extends Filter> getFilters() {
+ return filters;
}
public FilterNode findChain(String application, String serviceName) {
- FilterNode filterNode = microserviceChains.get(serviceName);
- if (filterNode == null) {
- List serviceFilters = filters.entrySet().stream()
- .filter(e -> e.getValue().enabledForMicroservice(application, serviceName))
- .map(e -> e.getValue())
- .collect(Collectors.toList());
- serviceFilters.sort(Comparator.comparingInt(a -> a.getOrder(invocationType, application, serviceName)));
- filterNode = FilterNode.buildChain(serviceFilters);
- microserviceChains.put(serviceName, filterNode);
- }
- return filterNode;
+ return microserviceChains.computeIfAbsent(application, key -> new ConcurrentHashMapEx<>())
+ .computeIfAbsent(serviceName, (serviceNameInner) -> {
+ List serviceFilters = filters.stream()
+ .filter(e -> e.enabledForMicroservice(application, serviceName))
+ .sorted(Comparator.comparingInt(a -> a.getOrder(application, serviceName)))
+ .collect(Collectors.toList());
+ return FilterNode.buildChain(serviceFilters);
+ });
}
}
diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/ProviderFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/ProviderFilter.java
index 7e1dbc318cb..6d5edd7814e 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/ProviderFilter.java
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/ProviderFilter.java
@@ -16,11 +16,6 @@
*/
package org.apache.servicecomb.core.filter;
-import org.apache.servicecomb.swagger.invocation.InvocationType;
-
public interface ProviderFilter extends Filter {
- @Override
- default boolean enabledForInvocationType(InvocationType invocationType) {
- return invocationType == InvocationType.PROVIDER;
- }
+
}
diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ContextMapperFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ContextMapperFilter.java
new file mode 100644
index 00000000000..a14e41dd660
--- /dev/null
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ContextMapperFilter.java
@@ -0,0 +1,84 @@
+/*
+ * 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.servicecomb.core.filter.impl;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.servicecomb.core.CoreConst;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.filter.AbstractFilter;
+import org.apache.servicecomb.core.filter.EdgeFilter;
+import org.apache.servicecomb.core.filter.FilterNode;
+import org.apache.servicecomb.core.filter.ProviderFilter;
+import org.apache.servicecomb.core.governance.MatchType;
+import org.apache.servicecomb.governance.handler.MapperHandler;
+import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor;
+import org.apache.servicecomb.governance.processor.mapping.Mapper;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.springframework.util.CollectionUtils;
+
+public class ContextMapperFilter extends AbstractFilter implements ProviderFilter, EdgeFilter {
+ private final MapperHandler mapperHandler;
+
+ public ContextMapperFilter(MapperHandler mapperHandler) {
+ this.mapperHandler = mapperHandler;
+ }
+
+ @Override
+ public boolean enabledForTransport(String transport) {
+ return CoreConst.RESTFUL.equals(transport);
+ }
+
+ @Override
+ public int getOrder() {
+ return -1995;
+ }
+
+ @Override
+ public String getName() {
+ return "context-mapper";
+ }
+
+ @Override
+ public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) {
+ GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation);
+ Mapper mapper = mapperHandler.getActuator(request);
+ if (mapper == null || CollectionUtils.isEmpty(mapper.target())) {
+ return nextNode.onFilter(invocation);
+ }
+ Map properties = mapper.target();
+ properties.forEach((k, v) -> {
+ if (StringUtils.isEmpty(v)) {
+ return;
+ }
+ if ("$U".equals(v)) {
+ invocation.addContext(k, request.apiPath());
+ } else if ("$M".equals(v)) {
+ invocation.addContext(k, request.method());
+ } else if (v.startsWith("$H{") && v.endsWith("}")) {
+ invocation.addContext(k, request.header(v.substring(3, v.length() - 1)));
+ } else if (v.startsWith("$Q{") && v.endsWith("}")) {
+ invocation.addContext(k, request.query(v.substring(3, v.length() - 1)));
+ } else {
+ invocation.addContext(k, v);
+ }
+ });
+ return nextNode.onFilter(invocation);
+ }
+}
diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.java
index f611616b4e3..d2b8eb82319 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.java
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.java
@@ -19,12 +19,15 @@
import java.util.concurrent.CompletableFuture;
import org.apache.servicecomb.core.Invocation;
-import org.apache.servicecomb.core.filter.Filter;
+import org.apache.servicecomb.core.filter.AbstractFilter;
+import org.apache.servicecomb.core.filter.ConsumerFilter;
+import org.apache.servicecomb.core.filter.EdgeFilter;
import org.apache.servicecomb.core.filter.FilterNode;
+import org.apache.servicecomb.core.filter.ProviderFilter;
import org.apache.servicecomb.swagger.invocation.Response;
// just for test
-public class EmptyFilter implements Filter {
+public class EmptyFilter extends AbstractFilter implements ProviderFilter, ConsumerFilter, EdgeFilter {
@Override
public String getName() {
return "empty";
@@ -34,4 +37,9 @@ public String getName() {
public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) {
return CompletableFuture.completedFuture(Response.ok(null));
}
+
+ @Override
+ public int getOrder() {
+ return 0;
+ }
}
diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java
index bce1b014de9..b4bf065792f 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java
@@ -17,16 +17,18 @@
package org.apache.servicecomb.core.filter.impl;
import java.lang.reflect.Method;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
+import org.apache.servicecomb.config.ConfigUtil;
import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.filter.AbstractFilter;
import org.apache.servicecomb.core.filter.Filter;
import org.apache.servicecomb.core.filter.FilterNode;
import org.apache.servicecomb.core.filter.ProviderFilter;
import org.apache.servicecomb.foundation.common.utils.AsyncUtils;
import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation;
-import org.apache.servicecomb.swagger.invocation.InvocationType;
import org.apache.servicecomb.swagger.invocation.Response;
import org.hibernate.validator.HibernateValidator;
import org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator;
@@ -35,39 +37,56 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.env.Environment;
+import jakarta.validation.Configuration;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
+import jakarta.validation.Valid;
import jakarta.validation.Validation;
import jakarta.validation.ValidatorFactory;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
import jakarta.validation.executable.ExecutableValidator;
import jakarta.validation.groups.Default;
-public class ParameterValidatorFilter implements ProviderFilter, InitializingBean {
+public class ParameterValidatorFilter extends AbstractFilter implements ProviderFilter, InitializingBean {
+ private static class Service {
+ @SuppressWarnings("unused")
+ public void service(@Valid Model model) {
+
+ }
+ }
+
+ private static class Model {
+ @NotNull
+ String name;
+
+ @Min(10)
+ int age;
+
+ Model(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+ }
+
private static final Logger LOGGER = LoggerFactory.getLogger(ParameterValidatorFilter.class);
public static final String NAME = "validator";
+ public static final String HIBERNATE_VALIDATE_PREFIX = "hibernate.validator";
+
public static final String ENABLE_EL = "servicecomb.filters.validation.useResourceBundleMessageInterpolator";
protected ExecutableValidator validator;
- private Environment environment;
-
- @Autowired
- public void setEnvironment(Environment environment) {
- this.environment = environment;
- }
-
@Override
public String getName() {
return NAME;
}
@Override
- public int getOrder(InvocationType invocationType, String application, String serviceName) {
+ public int getOrder() {
return Filter.PROVIDER_SCHEDULE_FILTER_ORDER + 1000;
}
@@ -75,14 +94,35 @@ public int getOrder(InvocationType invocationType, String application, String se
public void afterPropertiesSet() {
validator = createValidatorFactory()
.getValidator().forExecutables();
+ initValidate();
+ }
+
+ private void initValidate() {
+ // This method is intended to make first rpc call faster
+ // Because validation cache bean class, this way only make first rpc call a little faster.
+ try {
+ Model model = new Model("foo", 23);
+ Service instance = new Service();
+ Method method = Service.class.getMethod("service", Model.class);
+ Object[] args = new Object[] {model};
+ validator.validateParameters(instance, method, args, Default.class);
+ } catch (Throwable e) {
+ throw new IllegalStateException(e);
+ }
}
protected ValidatorFactory createValidatorFactory() {
- return Validation.byProvider(HibernateValidator.class)
+ Configuration> validatorConfiguration = Validation.byProvider(HibernateValidator.class)
.configure()
.propertyNodeNameProvider(new JacksonPropertyNodeNameProvider())
- .messageInterpolator(messageInterpolator())
- .buildValidatorFactory();
+ .messageInterpolator(messageInterpolator());
+ Map properties = ConfigUtil.stringPropertiesWithPrefix(environment, HIBERNATE_VALIDATE_PREFIX);
+ if (!properties.isEmpty()) {
+ for (Map.Entry entry : properties.entrySet()) {
+ validatorConfiguration.addProperty(entry.getKey(), entry.getValue());
+ }
+ }
+ return validatorConfiguration.buildValidatorFactory();
}
protected AbstractMessageInterpolator messageInterpolator() {
@@ -109,9 +149,7 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne
protected Set> doValidate(Invocation invocation) {
SwaggerProducerOperation producerOperation = invocation.getOperationMeta().getSwaggerProducerOperation();
- Object instance = producerOperation.getProducerInstance();
- Method method = producerOperation.getProducerMethod();
- Object[] args = invocation.toProducerArguments();
- return validator.validateParameters(instance, method, args, Default.class);
+ return validator.validateParameters(producerOperation.getProducerInstance(), producerOperation.getProducerMethod(),
+ invocation.toProducerArguments(), Default.class);
}
}
diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java
index bfd06f80eea..c8f757bf3e2 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java
@@ -21,16 +21,20 @@
import org.apache.servicecomb.core.Invocation;
import org.apache.servicecomb.core.exception.Exceptions;
+import org.apache.servicecomb.core.filter.AbstractFilter;
import org.apache.servicecomb.core.filter.Filter;
import org.apache.servicecomb.core.filter.FilterNode;
import org.apache.servicecomb.core.filter.ProviderFilter;
import org.apache.servicecomb.foundation.common.utils.AsyncUtils;
import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation;
-import org.apache.servicecomb.swagger.invocation.InvocationType;
import org.apache.servicecomb.swagger.invocation.Response;
import org.apache.servicecomb.swagger.invocation.context.ContextUtils;
+import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
-public class ProviderOperationFilter implements ProviderFilter {
+import jakarta.ws.rs.core.Response.Status;
+
+public class ProviderOperationFilter extends AbstractFilter implements ProviderFilter {
public static final String NAME = "producer-operation";
@Override
@@ -39,13 +43,17 @@ public String getName() {
}
@Override
- public int getOrder(InvocationType invocationType, String application, String serviceName) {
+ public int getOrder() {
// almost time, should be the last filter.
return Filter.PROVIDER_SCHEDULE_FILTER_ORDER + 2000;
}
@Override
public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) {
+ if (!transportAccessAllowed(invocation)) {
+ return CompletableFuture.failedFuture(new InvocationException(Status.UNAUTHORIZED,
+ new CommonExceptionData("transport access not allowed.")));
+ }
invocation.onBusinessMethodStart();
SwaggerProducerOperation producerOperation = invocation.getOperationMeta().getSwaggerProducerOperation();
@@ -57,6 +65,13 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne
.whenComplete((response, throwable) -> processMetrics(invocation));
}
+ private boolean transportAccessAllowed(Invocation invocation) {
+ if (invocation.getProviderTransportName() == null) {
+ return true;
+ }
+ return invocation.getProviderTransportName().equals(invocation.getTransportName());
+ }
+
@SuppressWarnings("unchecked")
protected CompletableFuture