diff --git a/.artifacts b/.artifacts new file mode 100644 index 0000000000..7ad4272959 --- /dev/null +++ b/.artifacts @@ -0,0 +1,107 @@ +# 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. +# + +# Please add new modules to the end of the list. + +dubbo +dubbo-auth +dubbo-apache-release +dubbo-bom +dubbo-build-tools +dubbo-cluster +dubbo-common +dubbo-compatible +dubbo-compiler +dubbo-config +dubbo-config-api +dubbo-config-spring +dubbo-configcenter +dubbo-configcenter-apollo +dubbo-configcenter-nacos +dubbo-configcenter-zookeeper +dubbo-container +dubbo-container-api +dubbo-container-spring +dubbo-core-spi +dubbo-dependencies +dubbo-dependencies-all +dubbo-dependencies-bom +dubbo-dependencies-zookeeper +dubbo-dependencies-zookeeper-curator5 +dubbo-distribution +dubbo-filter +dubbo-filter-cache +dubbo-filter-validation +dubbo-kubernetes +dubbo-maven-plugin +dubbo-metadata +dubbo-metadata-api +dubbo-metadata-definition-protobuf +dubbo-metadata-processor +dubbo-metadata-report-nacos +dubbo-metadata-report-redis +dubbo-metadata-report-zookeeper +dubbo-metrics +dubbo-metrics-api +dubbo-metrics-default +dubbo-metrics-metadata +dubbo-metrics-prometheus +dubbo-metrics-registry +dubbo-monitor +dubbo-monitor-api +dubbo-monitor-default +dubbo-native +dubbo-native-plugin +dubbo-parent +dubbo-plugin +dubbo-qos +dubbo-qos-api +dubbo-reactive +dubbo-registry +dubbo-registry-api +dubbo-registry-multicast +dubbo-registry-multiple +dubbo-registry-nacos +dubbo-registry-zookeeper +dubbo-remoting +dubbo-remoting-api +dubbo-remoting-http +dubbo-remoting-netty +dubbo-remoting-netty4 +dubbo-remoting-zookeeper +dubbo-remoting-zookeeper-curator5 +dubbo-rpc +dubbo-rpc-api +dubbo-rpc-dubbo +dubbo-rpc-injvm +dubbo-rpc-rest +dubbo-rpc-triple +dubbo-security +dubbo-serialization +dubbo-serialization-api +dubbo-serialization-fastjson2 +dubbo-serialization-hessian2 +dubbo-serialization-jdk +dubbo-spring-boot +dubbo-spring-boot-actuator +dubbo-spring-boot-actuator-compatible +dubbo-spring-boot-autoconfigure +dubbo-spring-boot-autoconfigure-compatible +dubbo-spring-boot-compatible +dubbo-spring-boot-observability-starter +dubbo-spring-boot-starter +dubbo-spring-security +dubbo-xds diff --git a/.github/workflows/build-and-test-pr.yml b/.github/workflows/build-and-test-pr.yml index 72c5fef4ca..853540e490 100644 --- a/.github/workflows/build-and-test-pr.yml +++ b/.github/workflows/build-and-test-pr.yml @@ -73,7 +73,9 @@ jobs: cd ./dubbo ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean source:jar install -Pjacoco,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Pack class result" - run: 7z a ${{ github.workspace }}/class.zip */target/classes/* -r + run: | + shopt -s globstar + zip ${{ github.workspace }}/class.zip **/target/classes/* -r - name: "Upload class result" uses: actions/upload-artifact@v3 with: @@ -81,7 +83,7 @@ jobs: path: ${{ github.workspace }}/class.zip - name: "Pack checkstyle file if failure" if: failure() - run: 7z a ${{ github.workspace }}/checkstyle.zip *checkstyle* -r + run: zip ${{ github.workspace }}/checkstyle.zip *checkstyle* -r - name: "Upload checkstyle file if failure" if: failure() uses: actions/upload-artifact@v3 diff --git a/.github/workflows/build-and-test-scheduled-3.1.yml b/.github/workflows/build-and-test-scheduled-3.1.yml index 7b1d67449f..8d56ddc30b 100644 --- a/.github/workflows/build-and-test-scheduled-3.1.yml +++ b/.github/workflows/build-and-test-scheduled-3.1.yml @@ -396,7 +396,7 @@ jobs: run: | cd ${{ github.workspace }}/dubbo/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider ${{ github.workspace }}/dubbo/mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean package -P native -Dmaven.test.skip=true native:compile - nohup ./target/dubbo-demo-native-provider & + nohup ./target/demo-native-provider & cd ${{ github.workspace }}/dubbo/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer ${{ github.workspace }}/dubbo/mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean package -P native -Dmaven.test.skip=true native:compile - ./target/dubbo-demo-native-consumer + ./target/demo-native-consumer diff --git a/dubbo-cluster/pom.xml b/dubbo-cluster/pom.xml index 8beed95576..130b7e3f7e 100644 --- a/dubbo-cluster/pom.xml +++ b/dubbo-cluster/pom.xml @@ -80,5 +80,16 @@ ${project.parent.version} compile + + org.apache.dubbo + dubbo-metrics-default + ${project.parent.version} + true + + + io.micrometer + micrometer-tracing-integration-test + test + diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java index 2e4d46865a..7a12e0f379 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java @@ -23,6 +23,7 @@ import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfigItem; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -68,7 +69,7 @@ public class ConfigParser { private static List parseJsonArray(String rawConfig) { List urls = new ArrayList<>(); try { - List list = JsonUtils.getJson().toJavaList(rawConfig, String.class); + List list = JsonUtils.toJavaList(rawConfig, String.class); if (!CollectionUtils.isEmpty(list)) { list.forEach(u -> urls.add(URL.valueOf(u))); } @@ -79,7 +80,7 @@ public class ConfigParser { } private static ConfiguratorConfig parseObject(String rawConfig) { - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Map map = yaml.load(rawConfig); return ConfiguratorConfig.parseFromMap(map); } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java index 9109286382..ee15fc6382 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java @@ -27,9 +27,9 @@ import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; -import org.apache.dubbo.metrics.event.MetricsEvent; +import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.registry.event.RegistryEvent; +import org.apache.dubbo.metrics.registry.event.type.ServiceType; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcContext; @@ -45,6 +45,7 @@ import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -131,8 +132,6 @@ public abstract class AbstractDirectory implements Directory { */ private final int reconnectTaskPeriod; - private final GlobalMetricsEventMulticaster eventMulticaster; - private ApplicationModel applicationModel; public AbstractDirectory(URL url) { @@ -187,7 +186,6 @@ public abstract class AbstractDirectory implements Directory { this.reconnectTaskPeriod = configuration.getInt(RECONNECT_TASK_PERIOD, DEFAULT_RECONNECT_TASK_PERIOD); setRouterChain(routerChain); - eventMulticaster = applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class); } @Override @@ -354,6 +352,7 @@ public abstract class AbstractDirectory implements Directory { } }, reconnectTaskPeriod, TimeUnit.MILLISECONDS); } + MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary())); } /** @@ -367,6 +366,7 @@ public abstract class AbstractDirectory implements Directory { if (invokersInitialized) { refreshInvokerInternal(); } + MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary())); } private synchronized void refreshInvokerInternal() { @@ -388,25 +388,18 @@ public abstract class AbstractDirectory implements Directory { invokersToRemove.removeAll(needToRemove); } - private void publishMetricsEvent(MetricsEvent event) { - if (eventMulticaster != null) { - eventMulticaster.publishEvent(event); - } - } - @Override public void addDisabledInvoker(Invoker invoker) { - publishMetricsEvent(new RegistryEvent.MetricsDirectoryEvent(applicationModel, RegistryEvent.ApplicationType.D_DISABLE)); if (invokers.contains(invoker)) { disabledInvokers.add(invoker); removeValidInvoker(invoker); logger.info("Disable service address: " + invoker.getUrl() + "."); } + MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary())); } @Override public void recoverDisabledInvoker(Invoker invoker) { - publishMetricsEvent(new RegistryEvent.MetricsDirectoryEvent(applicationModel, RegistryEvent.ApplicationType.D_RECOVER_DISABLE)); if (disabledInvokers.remove(invoker)) { try { addValidInvoker(invoker); @@ -415,6 +408,7 @@ public abstract class AbstractDirectory implements Directory { } } + MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary())); } protected final void refreshRouter(BitList> newlyInvokers, Runnable switchAction) { @@ -470,7 +464,8 @@ public abstract class AbstractDirectory implements Directory { this.invokers = invokers; refreshInvokerInternal(); this.invokersInitialized = true; - publishMetricsEvent(new RegistryEvent.MetricsDirectoryEvent(applicationModel, RegistryEvent.ApplicationType.D_CURRENT, invokers.size())); + + MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary())); } protected void destroyInvokers() { @@ -481,14 +476,12 @@ public abstract class AbstractDirectory implements Directory { } private boolean addValidInvoker(Invoker invoker) { - publishMetricsEvent(new RegistryEvent.MetricsDirectoryEvent(applicationModel, RegistryEvent.ApplicationType.D_VALID)); synchronized (this.validInvokers) { return this.validInvokers.add(invoker); } } private boolean removeValidInvoker(Invoker invoker) { - publishMetricsEvent(new RegistryEvent.MetricsDirectoryEvent(applicationModel, RegistryEvent.ApplicationType.D_UN_VALID)); synchronized (this.validInvokers) { return this.validInvokers.remove(invoker); } @@ -508,4 +501,29 @@ public abstract class AbstractDirectory implements Directory { .map(URL::getAddress) .collect(Collectors.joining(",")); } + + private Map> getSummary() { + Map> summaryMap = new HashMap<>(); + + summaryMap.put(ServiceType.D_VALID, groupByServiceKey(getValidInvokers())); + summaryMap.put(ServiceType.D_DISABLE, groupByServiceKey(getDisabledInvokers())); + summaryMap.put(ServiceType.D_TO_RECONNECT, groupByServiceKey(getInvokersToReconnect())); + summaryMap.put(ServiceType.D_ALL, groupByServiceKey(getInvokers())); + return summaryMap; + } + + private Map groupByServiceKey(Collection> invokers) { + + Map serviceNumMap = new HashMap<>(); + for (Invoker invoker : invokers) { + if (invoker.getClass().getSimpleName().contains("Mockito")) { + return serviceNumMap; + } + } + if (invokers.size() > 0) { + serviceNumMap = invokers.stream().filter(invoker -> invoker.getInterface() != null).collect(Collectors.groupingBy(invoker -> invoker.getInterface().getName(), Collectors.reducing(0, e -> 1, Integer::sum))); + } + + return serviceNumMap; + } } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java index d8a022f3d2..8a17dc0fc0 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java @@ -16,8 +16,6 @@ */ package org.apache.dubbo.rpc.cluster.directory; -import java.util.List; - import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; @@ -29,6 +27,8 @@ import org.apache.dubbo.rpc.cluster.RouterChain; import org.apache.dubbo.rpc.cluster.SingleRouterChain; import org.apache.dubbo.rpc.cluster.router.state.BitList; +import java.util.List; + import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_SITE_SELECTION; /** @@ -75,6 +75,8 @@ public class StaticDirectory extends AbstractDirectory { for (Invoker invoker : getValidInvokers()) { if (invoker.isAvailable()) { return true; + } else { + addInvalidateInvoker(invoker); } } return false; diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/filter/MetricsClusterFilter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/MetricsClusterFilter.java similarity index 95% rename from dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/filter/MetricsClusterFilter.java rename to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/MetricsClusterFilter.java index 4fa30ee0b2..2a4d36dcfc 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/filter/MetricsClusterFilter.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/MetricsClusterFilter.java @@ -15,7 +15,8 @@ * limitations under the License. */ -package org.apache.dubbo.metrics.filter; +package org.apache.dubbo.rpc.cluster.filter.support; + import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; @@ -29,12 +30,13 @@ import org.apache.dubbo.rpc.cluster.filter.ClusterFilter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; + import java.util.Optional; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; -@Activate(group = CONSUMER) +@Activate(group = CONSUMER,onClass = "org.apache.dubbo.metrics.collector.DefaultMetricsCollector") public class MetricsClusterFilter implements ClusterFilter, BaseFilter.Listener, ScopeModelAware { private DefaultMetricsCollector collector; diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/observation/ObservationSenderFilter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ObservationSenderFilter.java similarity index 62% rename from dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/observation/ObservationSenderFilter.java rename to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ObservationSenderFilter.java index 6e0e099e48..233a5ed35b 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/observation/ObservationSenderFilter.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ObservationSenderFilter.java @@ -14,12 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.metrics.observation; - -import io.micrometer.observation.Observation; -import io.micrometer.observation.ObservationRegistry; +package org.apache.dubbo.rpc.cluster.filter.support; import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metrics.observation.DefaultDubboClientObservationConvention; +import org.apache.dubbo.metrics.observation.DubboClientContext; +import org.apache.dubbo.metrics.observation.DubboClientObservationConvention; +import org.apache.dubbo.metrics.observation.DubboObservationDocumentation; import org.apache.dubbo.rpc.BaseFilter; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; @@ -30,6 +31,9 @@ import org.apache.dubbo.rpc.cluster.filter.ClusterFilter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationRegistry; + import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; /** @@ -38,13 +42,17 @@ import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; @Activate(group = CONSUMER, order = -1, onClass = "io.micrometer.observation.NoopObservationRegistry") public class ObservationSenderFilter implements ClusterFilter, BaseFilter.Listener, ScopeModelAware { - private final ObservationRegistry observationRegistry; + private ObservationRegistry observationRegistry; - private final DubboClientObservationConvention clientObservationConvention; + private DubboClientObservationConvention clientObservationConvention; public ObservationSenderFilter(ApplicationModel applicationModel) { - observationRegistry = applicationModel.getBeanFactory().getBean(ObservationRegistry.class); - clientObservationConvention = applicationModel.getBeanFactory().getBean(DubboClientObservationConvention.class); + applicationModel.getApplicationConfigManager().getTracing().ifPresent(cfg -> { + if (Boolean.TRUE.equals(cfg.getEnabled())) { + observationRegistry = applicationModel.getBeanFactory().getBean(ObservationRegistry.class); + clientObservationConvention = applicationModel.getBeanFactory().getBean(DubboClientObservationConvention.class); + } + }); } @Override @@ -52,15 +60,18 @@ public class ObservationSenderFilter implements ClusterFilter, BaseFilter.Listen if (observationRegistry == null) { return invoker.invoke(invocation); } - DubboClientContext senderContext = new DubboClientContext(invoker, invocation); - Observation observation = DubboObservation.CLIENT.observation(this.clientObservationConvention, DefaultDubboClientObservationConvention.INSTANCE, () -> senderContext, observationRegistry); + final DubboClientContext senderContext = new DubboClientContext(invoker, invocation); + final Observation observation = DubboObservationDocumentation.CLIENT.observation( + this.clientObservationConvention, + DefaultDubboClientObservationConvention.getInstance(), + () -> senderContext, observationRegistry); invocation.put(Observation.class, observation.start()); return observation.scoped(() -> invoker.invoke(invocation)); } @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { - Observation observation = (Observation) invocation.get(Observation.class); + final Observation observation = getObservation(invocation); if (observation == null) { return; } @@ -69,11 +80,15 @@ public class ObservationSenderFilter implements ClusterFilter, BaseFilter.Listen @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { - Observation observation = (Observation) invocation.get(Observation.class); + final Observation observation = getObservation(invocation); if (observation == null) { return; } observation.error(t); observation.stop(); } + + private Observation getObservation(Invocation invocation) { + return (Observation) invocation.get(Observation.class); + } } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java index cc06c0d902..ce5bdaa16c 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java @@ -22,6 +22,7 @@ import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.ClusterInvoker; +import java.util.Arrays; import java.util.List; import java.util.concurrent.ThreadLocalRandom; @@ -79,10 +80,23 @@ public class RandomLoadBalance extends AbstractLoadBalance { // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight. int offset = ThreadLocalRandom.current().nextInt(totalWeight); // Return an invoker based on the random value. - for (int i = 0; i < length; i++) { - if (offset < weights[i]) { - return invokers.get(i); + if (length <= 4) { + for (int i = 0; i < length; i++) { + if (offset < weights[i]) { + return invokers.get(i); + } } + } else { + int i = Arrays.binarySearch(weights, offset); + if (i < 0) { + i = -i - 1; + } else { + while (weights[i+1] == offset) { + i++; + } + i++; + } + return invokers.get(i); } } // If all invokers have the same weight value or totalWeight=0, return evenly. diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java index 3d6c53ac57..699a18b90a 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java @@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.cluster.router.condition.config.model; import org.apache.dubbo.common.utils.CollectionUtils; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -40,7 +41,7 @@ import java.util.Map; public class ConditionRuleParser { public static ConditionRouterRule parse(String rawRule) { - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Map map = yaml.load(rawRule); ConditionRouterRule rule = ConditionRouterRule.parseFromMap(map); rule.setRawRule(rawRule); diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java index b6332e4485..f49f8e9b16 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java @@ -26,6 +26,8 @@ import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleDispatcher; import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleListener; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.representer.Representer; @@ -66,9 +68,9 @@ public class MeshAppRuleListener implements ConfigurationListener { try { Map>> groupMap = new HashMap<>(); - Representer representer = new Representer(); + Representer representer = new Representer(new DumperOptions()); representer.getPropertyUtils().setSkipMissingProperties(true); - Yaml yaml = new Yaml(new SafeConstructor(), representer); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()), representer); Iterable yamlIterator = yaml.loadAll(configInfo); for (Object obj : yamlIterator) { diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/config/model/ScriptRule.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/config/model/ScriptRule.java index 5ebf7217ee..1ee885e456 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/config/model/ScriptRule.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/config/model/ScriptRule.java @@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.cluster.router.script.config.model; import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -30,7 +31,7 @@ public class ScriptRule extends AbstractRouterRule { private String script; public static ScriptRule parse(String rawRule) { - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Map map = yaml.load(rawRule); ScriptRule rule = new ScriptRule(); diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java index a83d2f5476..5c9712acc8 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java @@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.cluster.router.tag.model; import org.apache.dubbo.common.utils.CollectionUtils; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -29,7 +30,7 @@ import java.util.Map; public class TagRuleParser { public static TagRouterRule parse(String rawRule) { - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Map map = yaml.load(rawRule); TagRouterRule rule = TagRouterRule.parseFromMap(map); rule.setRawRule(rawRule); diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/ScopeClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/ScopeClusterInvoker.java index 02a96e810b..445aaf236b 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/ScopeClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/ScopeClusterInvoker.java @@ -39,7 +39,6 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; -import static org.apache.dubbo.rpc.Constants.LOCAL_KEY; import static org.apache.dubbo.rpc.Constants.LOCAL_PROTOCOL; import static org.apache.dubbo.rpc.Constants.SCOPE_KEY; import static org.apache.dubbo.rpc.Constants.SCOPE_REMOTE; @@ -47,24 +46,23 @@ import static org.apache.dubbo.rpc.Constants.SCOPE_LOCAL; import static org.apache.dubbo.rpc.cluster.Constants.PEER_KEY; /** - * A ClusterInvoker that selects between local and remote invokers at runtime. + * ScopeClusterInvoker is a cluster invoker which handles the invocation logic of a single service in a specific scope. + *

+ * It selects between local and remote invoker at runtime. + * + * @param the type of service interface */ public class ScopeClusterInvoker implements ClusterInvoker, ExporterChangeListener { - + private final Object createLock = new Object(); private Protocol protocolSPI; private final Directory directory; private final Invoker invoker; private final AtomicBoolean isExported; private volatile Invoker injvmInvoker; private volatile InjvmExporterListener injvmExporterListener; - private boolean peerFlag; - private boolean injvmFlag; - private final Object createLock = new Object(); - - public ScopeClusterInvoker(Directory directory, Invoker invoker) { this.directory = directory; this.invoker = invoker; @@ -72,30 +70,6 @@ public class ScopeClusterInvoker implements ClusterInvoker, ExporterChange init(); } - private void init() { - Boolean peer = (Boolean) getUrl().getAttribute(PEER_KEY); - String isInjvm = getUrl().getParameter(LOCAL_PROTOCOL); - if (peer != null && peer) { - peerFlag = true; - return; - } - if (injvmInvoker == null && LOCAL_PROTOCOL.equalsIgnoreCase(getRegistryUrl().getProtocol())) { - injvmInvoker = invoker; - isExported.compareAndSet(false, true); - injvmFlag = true; - return; - } - if (Boolean.TRUE.toString().equalsIgnoreCase(isInjvm) || LOCAL_KEY.equalsIgnoreCase(getUrl().getParameter(SCOPE_KEY))) { - injvmFlag = true; - } else if (isInjvm == null) { - injvmFlag = isNotRemoteOrGeneric(); - } - - protocolSPI = getUrl().getApplicationModel().getExtensionLoader(Protocol.class).getAdaptiveExtension(); - injvmExporterListener = getUrl().getOrDefaultFrameworkModel().getBeanFactory().getBean(InjvmExporterListener.class); - injvmExporterListener.addExporterChangeListener(this, getUrl().getServiceKey()); - } - @Override public URL getUrl() { return directory.getConsumerUrl(); @@ -135,62 +109,28 @@ public class ScopeClusterInvoker implements ClusterInvoker, ExporterChange return directory.getInterface(); } + /** + * Checks if the current ScopeClusterInvoker is exported to the local JVM and invokes the corresponding Invoker. + * If it's not exported locally, then it delegates the invocation to the original Invoker. + * + * @param invocation the invocation to be performed + * @return the result of the invocation + * @throws RpcException if there was an error during the invocation + */ @Override public Result invoke(Invocation invocation) throws RpcException { if (peerFlag) { + // If it's a point-to-point direct connection, invoke the original Invoker return invoker.invoke(invocation); } if (isInjvmExported()) { + // If it's exported to the local JVM, invoke the corresponding Invoker return injvmInvoker.invoke(invocation); } + // Otherwise, delegate the invocation to the original Invoker return invoker.invoke(invocation); } - private boolean isNotRemoteOrGeneric() { - return !SCOPE_REMOTE.equalsIgnoreCase(getUrl().getParameter(SCOPE_KEY)) && - !getUrl().getParameter(GENERIC_KEY, false); - } - - private boolean isInjvmExported() { - Boolean localInvoke = RpcContext.getServiceContext().getLocalInvoke(); - boolean isExportedValue = isExported.get(); - boolean local = (localInvoke != null && localInvoke); - // Determine whether this call is local - if (isExportedValue && local) { - return true; - } - - // Determine whether this call is remote - if (localInvoke != null && !localInvoke) { - return false; - } - - // When calling locally, determine whether it does not meet the requirements - if (!isExportedValue && (SCOPE_LOCAL.equalsIgnoreCase(getUrl().getParameter(SCOPE_KEY)) || - Boolean.TRUE.toString().equalsIgnoreCase(getUrl().getParameter(LOCAL_PROTOCOL))|| local)) { - throw new RpcException("Local service has not been exposed yet!"); - } - - return isExportedValue && injvmFlag; - } - - - private void createInjvmInvoker() { - if (injvmInvoker == null) { - synchronized (createLock) { - if (injvmInvoker == null) { - URL url = new ServiceConfigURL(LOCAL_PROTOCOL, NetUtils.getLocalHost(), getUrl().getPort(), getInterface().getName(), getUrl().getParameters()); - url = url.setScopeModel(getUrl().getScopeModel()); - url = url.setServiceModel(getUrl().getServiceModel()); - Invoker invoker = protocolSPI.refer(getInterface(), url); - List> invokers = new ArrayList<>(); - invokers.add(invoker); - injvmInvoker = Cluster.getCluster(url.getScopeModel(), Cluster.DEFAULT, false).join(new StaticDirectory(url, invokers), true); - } - } - } - } - @Override public void onExporterChangeExport(Exporter exporter) { if (isExported.get()) { @@ -212,6 +152,103 @@ public class ScopeClusterInvoker implements ClusterInvoker, ExporterChange } } + public Invoker getInvoker() { + return invoker; + } + + /** + * Initializes the ScopeClusterInvoker instance. + */ + private void init() { + Boolean peer = (Boolean) getUrl().getAttribute(PEER_KEY); + String isInjvm = getUrl().getParameter(LOCAL_PROTOCOL); + // When the point-to-point direct connection is directly connected, + // the initialization is directly ended + if (peer != null && peer) { + peerFlag = true; + return; + } + // Check if the service has been exported through Injvm protocol + if (injvmInvoker == null && LOCAL_PROTOCOL.equalsIgnoreCase(getRegistryUrl().getProtocol())) { + injvmInvoker = invoker; + isExported.compareAndSet(false, true); + injvmFlag = true; + return; + } + // Check if the service has been exported through Injvm protocol or the SCOPE_LOCAL parameter is set + if (Boolean.TRUE.toString().equalsIgnoreCase(isInjvm) || SCOPE_LOCAL.equalsIgnoreCase(getUrl().getParameter(SCOPE_KEY))) { + injvmFlag = true; + } else if (isInjvm == null) { + injvmFlag = isNotRemoteOrGeneric(); + } + + protocolSPI = getUrl().getApplicationModel().getExtensionLoader(Protocol.class).getAdaptiveExtension(); + injvmExporterListener = getUrl().getOrDefaultFrameworkModel().getBeanFactory().getBean(InjvmExporterListener.class); + injvmExporterListener.addExporterChangeListener(this, getUrl().getServiceKey()); + } + + /** + * Check if the service is a generalized call or the SCOPE_REMOTE parameter is set + * + * @return boolean + */ + private boolean isNotRemoteOrGeneric() { + return !SCOPE_REMOTE.equalsIgnoreCase(getUrl().getParameter(SCOPE_KEY)) && + !getUrl().getParameter(GENERIC_KEY, false); + } + + /** + * Checks whether the current ScopeClusterInvoker is exported to the local JVM and returns a boolean value. + * + * @return true if the ScopeClusterInvoker is exported to the local JVM, false otherwise + * @throws RpcException if there was an error during the invocation + */ + private boolean isInjvmExported() { + Boolean localInvoke = RpcContext.getServiceContext().getLocalInvoke(); + boolean isExportedValue = isExported.get(); + boolean local = (localInvoke != null && localInvoke); + // Determine whether this call is local + if (isExportedValue && local) { + return true; + } + + // Determine whether this call is remote + if (localInvoke != null && !localInvoke) { + return false; + } + + // When calling locally, determine whether it does not meet the requirements + if (!isExportedValue && (SCOPE_LOCAL.equalsIgnoreCase(getUrl().getParameter(SCOPE_KEY)) || + Boolean.TRUE.toString().equalsIgnoreCase(getUrl().getParameter(LOCAL_PROTOCOL)) || local)) { + // If it's supposed to be exported to the local JVM ,but it's not, throw an exception + throw new RpcException("Local service for " + getUrl().getServiceInterface() + " has not been exposed yet!"); + } + + return isExportedValue && injvmFlag; + } + + /** + * Creates a new Invoker for the current ScopeClusterInvoker and exports it to the local JVM. + */ + private void createInjvmInvoker() { + if (injvmInvoker == null) { + synchronized (createLock) { + if (injvmInvoker == null) { + URL url = new ServiceConfigURL(LOCAL_PROTOCOL, NetUtils.getLocalHost(), getUrl().getPort(), getInterface().getName(), getUrl().getParameters()); + url = url.setScopeModel(getUrl().getScopeModel()); + url = url.setServiceModel(getUrl().getServiceModel()); + Invoker invoker = protocolSPI.refer(getInterface(), url); + List> invokers = new ArrayList<>(); + invokers.add(invoker); + injvmInvoker = Cluster.getCluster(url.getScopeModel(), Cluster.DEFAULT, false).join(new StaticDirectory(url, invokers), true); + } + } + } + } + + /** + * Destroy the existing InjvmInvoker. + */ private void destroyInjvmInvoker() { if (injvmInvoker != null) { injvmInvoker.destroy(); @@ -219,7 +256,4 @@ public class ScopeClusterInvoker implements ClusterInvoker, ExporterChange } } - public Invoker getInvoker() { - return invoker; - } } diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Cluster b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Cluster index 5ff23dc553..75dbd9f694 100644 --- a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Cluster +++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Cluster @@ -8,4 +8,4 @@ forking=org.apache.dubbo.rpc.cluster.support.ForkingCluster available=org.apache.dubbo.rpc.cluster.support.AvailableCluster mergeable=org.apache.dubbo.rpc.cluster.support.MergeableCluster broadcast=org.apache.dubbo.rpc.cluster.support.BroadcastCluster -zone-aware=org.apache.dubbo.rpc.cluster.support.registry.ZoneAwareCluster +zone-aware=org.apache.dubbo.rpc.cluster.support.registry.ZoneAwareCluster \ No newline at end of file diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter index b21c3d843f..cd0a2f44e8 100644 --- a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter +++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter @@ -1,3 +1,5 @@ consumercontext=org.apache.dubbo.rpc.cluster.filter.support.ConsumerContextFilter consumer-classloader=org.apache.dubbo.rpc.cluster.filter.support.ConsumerClassLoaderFilter router-snapshot=org.apache.dubbo.rpc.cluster.router.RouterSnapshotFilter +observationsender=org.apache.dubbo.rpc.cluster.filter.support.ObservationSenderFilter +metricsClusterFilter=org.apache.dubbo.rpc.cluster.filter.support.MetricsClusterFilter \ No newline at end of file diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java index a7e50304c9..dd30a0a037 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java @@ -22,6 +22,7 @@ import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -50,7 +51,7 @@ class ConfigParserTest { @Test void snakeYamlBasicTest() throws IOException { try (InputStream yamlStream = this.getClass().getResourceAsStream("/ServiceNoApp.yml")) { - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Map map = yaml.load(yamlStream); ConfiguratorConfig config = ConfiguratorConfig.parseFromMap(map); Assertions.assertNotNull(config); diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectoryTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectoryTest.java index 62787ccd2b..68ee46afb1 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectoryTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectoryTest.java @@ -19,15 +19,15 @@ package org.apache.dubbo.rpc.cluster.directory; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.common.utils.NetUtils; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; +import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.router.MockInvoker; import org.apache.dubbo.rpc.cluster.router.condition.ConditionStateRouterFactory; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; - import org.apache.dubbo.rpc.model.ApplicationModel; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -52,9 +52,9 @@ class StaticDirectoryTest { List routers = new ArrayList(); routers.add(router); List> originInvokers = new ArrayList>(); - Invoker invoker1 = new MockInvoker(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService")); - Invoker invoker2 = new MockInvoker(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")); - Invoker invoker3 = new MockInvoker(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService")); + Invoker invoker1 = new MockInvoker(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"), true); + Invoker invoker2 = new MockInvoker(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService"), true); + Invoker invoker3 = new MockInvoker(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService"), true); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); @@ -62,10 +62,10 @@ class StaticDirectoryTest { List> filteredInvokers = router.route(invokers.clone(), URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); - ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class); + ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); StaticDirectory staticDirectory = new StaticDirectory<>(filteredInvokers); boolean isAvailable = staticDirectory.isAvailable(); - Assertions.assertTrue(!isAvailable); + Assertions.assertTrue(isAvailable); List> newInvokers = staticDirectory.list(new MockDirInvocation()); Assertions.assertTrue(newInvokers.size() > 0); staticDirectory.destroy(); diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/AbstractObservationFilterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/AbstractObservationFilterTest.java new file mode 100644 index 0000000000..2f293484b4 --- /dev/null +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/AbstractObservationFilterTest.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.cluster.filter; + +import io.micrometer.tracing.test.SampleTestRunner; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.TracingConfig; +import org.apache.dubbo.rpc.AppResponse; +import org.apache.dubbo.rpc.BaseFilter; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.junit.jupiter.api.AfterEach; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +abstract class AbstractObservationFilterTest extends SampleTestRunner { + + ApplicationModel applicationModel; + RpcInvocation invocation; + + BaseFilter filter; + + Invoker invoker = mock(Invoker.class); + + static final String INTERFACE_NAME = "org.apache.dubbo.MockInterface"; + static final String METHOD_NAME = "mockMethod"; + static final String GROUP = "mockGroup"; + static final String VERSION = "1.0.0"; + + @AfterEach + public void teardown() { + if (applicationModel != null) { + applicationModel.destroy(); + } + } + + abstract BaseFilter createFilter(ApplicationModel applicationModel); + + void setupConfig() { + ApplicationConfig config = new ApplicationConfig(); + config.setName("MockObservations"); + + applicationModel = ApplicationModel.defaultModel(); + applicationModel.getApplicationConfigManager().setApplication(config); + + invocation = new RpcInvocation(new MockInvocation()); + invocation.addInvokedInvoker(invoker); + + applicationModel.getBeanFactory().registerBean(getObservationRegistry()); + TracingConfig tracingConfig = new TracingConfig(); + tracingConfig.setEnabled(true); + applicationModel.getApplicationConfigManager().setTracing(tracingConfig); + + filter = createFilter(applicationModel); + + given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); + + initParam(); + } + + private void initParam() { + invocation.setTargetServiceUniqueName(GROUP + "/" + INTERFACE_NAME + ":" + VERSION); + invocation.setMethodName(METHOD_NAME); + invocation.setParameterTypes(new Class[] {String.class}); + } + +} diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilderTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilderTest.java index 7f0bb6ff07..3a173c2ceb 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilderTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilderTest.java @@ -49,7 +49,6 @@ class DefaultFilterChainBuilderTest { }; Invoker invokerAfterBuild = defaultFilterChainBuilder.buildInvokerChain(invokerWithoutFilter, REFERENCE_FILTER_KEY, CONSUMER); - Assertions.assertTrue(invokerAfterBuild instanceof AbstractInvoker); // verify that if LogFilter is configured, LogFilter should exist in the filter chain URL urlWithFilter = URL.valueOf("injvm://127.0.0.1/DemoService") @@ -64,8 +63,6 @@ class DefaultFilterChainBuilderTest { }; invokerAfterBuild = defaultFilterChainBuilder.buildInvokerChain(invokerWithFilter, REFERENCE_FILTER_KEY, CONSUMER); Assertions.assertTrue(invokerAfterBuild instanceof FilterChainBuilder.CallbackRegistrationInvoker); - Assertions.assertEquals(1, ((FilterChainBuilder.CallbackRegistrationInvoker) invokerAfterBuild).filters.size()); - } @Test @@ -84,7 +81,7 @@ class DefaultFilterChainBuilderTest { }; Invoker invokerAfterBuild = defaultFilterChainBuilder.buildInvokerChain(invokerWithoutFilter, REFERENCE_FILTER_KEY, CONSUMER); - Assertions.assertTrue(invokerAfterBuild instanceof AbstractInvoker); +// Assertions.assertTrue(invokerAfterBuild instanceof AbstractInvoker); // verify that if LogFilter is configured, LogFilter should exist in the filter chain URL urlWithFilter = URL.valueOf("dubbo://127.0.0.1:20880/DemoService") diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/MetricsClusterFilterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/MetricsClusterFilterTest.java new file mode 100644 index 0000000000..629e3fc6f1 --- /dev/null +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/MetricsClusterFilterTest.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.rpc.cluster.filter; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; +import org.apache.dubbo.metrics.filter.MetricsFilter; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.sample.CounterMetricSample; +import org.apache.dubbo.metrics.model.sample.MetricSample; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.cluster.filter.support.MetricsClusterFilter; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +class MetricsClusterFilterTest { + + private ApplicationModel applicationModel; + private MetricsFilter filter; + private MetricsClusterFilter metricsClusterFilter; + private DefaultMetricsCollector collector; + private RpcInvocation invocation; + private final Invoker invoker = mock(Invoker.class); + + private static final String INTERFACE_NAME = "org.apache.dubbo.MockInterface"; + private static final String METHOD_NAME = "mockMethod"; + private static final String GROUP = "mockGroup"; + private static final String VERSION = "1.0.0"; + private String side; + + private AtomicBoolean initApplication = new AtomicBoolean(false); + + + @BeforeEach + public void setup() { + ApplicationConfig config = new ApplicationConfig(); + config.setName("MockMetrics"); + //RpcContext.getContext().setAttachment("MockMetrics","MockMetrics"); + + applicationModel = ApplicationModel.defaultModel(); + applicationModel.getApplicationConfigManager().setApplication(config); + + invocation = new RpcInvocation(); + filter = new MetricsFilter(); + + collector = applicationModel.getBeanFactory().getOrRegisterBean(DefaultMetricsCollector.class); + if(!initApplication.get()) { + collector.collectApplication(applicationModel); + initApplication.set(true); + } + filter.setApplicationModel(applicationModel); + side = CommonConstants.CONSUMER; + invocation.setInvoker(new TestMetricsInvoker(side)); + RpcContext.getServiceContext().setUrl(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&side=" + side)); + + metricsClusterFilter = new MetricsClusterFilter(); + metricsClusterFilter.setApplicationModel(applicationModel); + } + + @AfterEach + public void teardown() { + applicationModel.destroy(); + } + + @Test + public void testNoProvider(){ + testClusterFilterError(RpcException.FORBIDDEN_EXCEPTION, + MetricsKey.METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED.getNameByType(CommonConstants.CONSUMER)); + } + + private void testClusterFilterError(int errorCode,String name){ + collector.setCollectEnabled(true); + given(invoker.invoke(invocation)).willThrow(new RpcException(errorCode)); + initParam(); + + Long count = 1L; + + for (int i = 0; i < count; i++) { + try { + metricsClusterFilter.invoke(invoker, invocation); + } catch (Exception e) { + Assertions.assertTrue(e instanceof RpcException); + metricsClusterFilter.onError(e, invoker, invocation); + } + } + Map metricsMap = getMetricsMap(); + Assertions.assertTrue(metricsMap.containsKey(name)); + + MetricSample sample = metricsMap.get(name); + + Assertions.assertSame(((CounterMetricSample) sample).getValue().longValue(), count); + teardown(); + } + + + + private void initParam() { + invocation.setTargetServiceUniqueName(GROUP + "/" + INTERFACE_NAME + ":" + VERSION); + invocation.setMethodName(METHOD_NAME); + invocation.setParameterTypes(new Class[]{String.class}); + } + + private Map getMetricsMap() { + List samples = collector.collect(); + List samples1 = new ArrayList<>(); + for (MetricSample sample : samples) { + if (sample.getName().contains("dubbo.thread.pool")) { + continue; + } + samples1.add(sample); + } + return samples1.stream().collect(Collectors.toMap(MetricSample::getName, Function.identity())); + } + + public class TestMetricsInvoker implements Invoker { + + private String side; + + public TestMetricsInvoker(String side) { + this.side = side; + } + + @Override + public Class getInterface() { + return null; + } + + @Override + public Result invoke(Invocation invocation) throws RpcException { + return null; + } + + @Override + public URL getUrl() { + return URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&side="+side); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public void destroy() { + + } + } +} diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/MockInvocation.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/MockInvocation.java new file mode 100644 index 0000000000..9bd89552e3 --- /dev/null +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/MockInvocation.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.cluster.filter; + +import org.apache.dubbo.rpc.AttachmentsAdapter; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.model.ServiceModel; + +import java.util.HashMap; +import java.util.Map; + +import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; +import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; + +/** + * MockInvocation.java + */ +public class MockInvocation extends RpcInvocation { + + private Map attachments; + + public MockInvocation() { + attachments = new HashMap<>(); + attachments.put(PATH_KEY, "dubbo"); + attachments.put(GROUP_KEY, "dubbo"); + attachments.put(VERSION_KEY, "1.0.0"); + attachments.put(DUBBO_VERSION_KEY, "1.0.0"); + attachments.put(TOKEN_KEY, "sfag"); + attachments.put(TIMEOUT_KEY, "1000"); + } + + @Override + public String getTargetServiceUniqueName() { + return null; + } + + @Override + public String getProtocolServiceKey() { + return null; + } + + public String getMethodName() { + return "echo"; + } + + @Override + public String getServiceName() { + return "DemoService"; + } + + public Class[] getParameterTypes() { + return new Class[] {String.class}; + } + + public Object[] getArguments() { + return new Object[] {"aa"}; + } + + public Map getAttachments() { + return new AttachmentsAdapter.ObjectToStringMap(attachments); + } + + @Override + public Map getObjectAttachments() { + return attachments; + } + + @Override + public void setAttachment(String key, String value) { + setObjectAttachment(key, value); + } + + @Override + public void setAttachment(String key, Object value) { + setObjectAttachment(key, value); + } + + @Override + public void setObjectAttachment(String key, Object value) { + attachments.put(key, value); + } + + @Override + public void setAttachmentIfAbsent(String key, String value) { + setObjectAttachmentIfAbsent(key, value); + } + + @Override + public void setAttachmentIfAbsent(String key, Object value) { + setObjectAttachmentIfAbsent(key, value); + } + + @Override + public void setObjectAttachmentIfAbsent(String key, Object value) { + attachments.put(key, value); + } + + public Invoker getInvoker() { + return null; + } + + @Override + public void setServiceModel(ServiceModel serviceModel) { + + } + + @Override + public ServiceModel getServiceModel() { + return null; + } + + @Override + public Object put(Object key, Object value) { + return null; + } + + @Override + public Object get(Object key) { + return null; + } + + @Override + public Map getAttributes() { + return null; + } + + public String getAttachment(String key) { + return (String) getObjectAttachments().get(key); + } + + @Override + public Object getObjectAttachment(String key) { + return attachments.get(key); + } + + public String getAttachment(String key, String defaultValue) { + return (String) getObjectAttachments().get(key); + } + + @Override + public Object getObjectAttachment(String key, Object defaultValue) { + Object result = attachments.get(key); + if (result == null) { + return defaultValue; + } + return result; + } + +} diff --git a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/observation/ObservationSenderFilterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/ObservationSenderFilterTest.java similarity index 92% rename from dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/observation/ObservationSenderFilterTest.java rename to dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/ObservationSenderFilterTest.java index e25c2412b6..8166203400 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/observation/ObservationSenderFilterTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/ObservationSenderFilterTest.java @@ -15,21 +15,22 @@ * limitations under the License. */ -package org.apache.dubbo.metrics.observation; +package org.apache.dubbo.rpc.cluster.filter; import io.micrometer.common.KeyValues; import io.micrometer.core.tck.MeterRegistryAssert; +import io.micrometer.tracing.test.SampleTestRunner; import io.micrometer.tracing.test.simple.SpansAssert; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.RpcContext; -import org.apache.dubbo.rpc.cluster.filter.ClusterFilter; +import org.apache.dubbo.rpc.cluster.filter.support.ObservationSenderFilter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.assertj.core.api.BDDAssertions; class ObservationSenderFilterTest extends AbstractObservationFilterTest { @Override - public SampleTestRunnerConsumer yourCode() { + public SampleTestRunner.SampleTestRunnerConsumer yourCode() { return (buildingBlocks, meterRegistry) -> { setupConfig(); setupAttachments(); diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LoadBalanceBaseTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LoadBalanceBaseTest.java index 4e71762ab0..564680472d 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LoadBalanceBaseTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LoadBalanceBaseTest.java @@ -224,7 +224,7 @@ class LoadBalanceBaseTest { @Override public String toString() { - return JsonUtils.getJson().toJson(this); + return JsonUtils.toJson(this); } } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java index 57b3162695..b025e8f8e9 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java @@ -18,7 +18,7 @@ package org.apache.dubbo.rpc.cluster.router.file; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; +import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; @@ -61,7 +61,7 @@ class FileRouterEngineTest { @BeforeAll public static void setUpBeforeClass() throws Exception { - ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class); + ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); System.setProperty(ENABLE_CONNECTIVITY_VALIDATION, "false"); } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java index d089d33a76..8619baf581 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java @@ -27,6 +27,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -132,7 +133,7 @@ class MeshAppRuleListenerTest { List> rulesReceived = ruleCaptor.getValue(); assertEquals(2, rulesReceived.size()); - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1))); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2))); @@ -163,7 +164,7 @@ class MeshAppRuleListenerTest { List> rulesReceived = ruleCaptor.getValue(); assertEquals(2, rulesReceived.size()); - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1))); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2))); @@ -219,7 +220,7 @@ class MeshAppRuleListenerTest { List> rulesReceived = ruleCaptor.getValue(); assertEquals(2, rulesReceived.size()); - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1))); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2))); @@ -258,7 +259,7 @@ class MeshAppRuleListenerTest { List> rulesReceived = ruleCaptor.getValue(); assertEquals(1, rulesReceived.size()); - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2))); meshAppRuleListener.receiveConfigInfo(rule1 + "---\n" + rule4); @@ -282,7 +283,7 @@ class MeshAppRuleListenerTest { @Override public void onRuleChange(String appName, List> rules) { Assertions.assertEquals("demo-route", appName); - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rules.contains(yaml.load(rule5))); Assertions.assertTrue(rules.contains(yaml.load(rule6))); count.incrementAndGet(); @@ -303,7 +304,7 @@ class MeshAppRuleListenerTest { @Override public void onRuleChange(String appName, List> rules) { Assertions.assertEquals("demo-route", appName); - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rules.contains(yaml.load(rule7))); Assertions.assertTrue(rules.contains(yaml.load(rule8))); count.incrementAndGet(); @@ -353,7 +354,7 @@ class MeshAppRuleListenerTest { List> rulesReceived = ruleCaptor.getValue(); assertEquals(2, rulesReceived.size()); - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1))); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2))); diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java index 4c07bddc27..ab609ec860 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java @@ -27,6 +27,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -166,7 +167,7 @@ class MeshRuleManagerTest { @Override public void onRuleChange(String appName, List> rules) { assertEquals("dubbo-demo", appName); - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); assertTrue(rules.contains(yaml.load(rule1))); assertTrue(rules.contains(yaml.load(rule2))); diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java index 979218beae..35f2a7e9ae 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java @@ -33,6 +33,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -239,7 +240,7 @@ class MeshRuleRouterTest { void testRuleChange() { StandardMeshRuleRouter meshRuleRouter = new StandardMeshRuleRouter<>(url); - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); List> rules = new LinkedList<>(); rules.add(yaml.load(rule1)); @@ -275,7 +276,7 @@ class MeshRuleRouterTest { void testRoute2() { StandardMeshRuleRouter meshRuleRouter = new StandardMeshRuleRouter<>(url); - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); List> rules = new LinkedList<>(); rules.add(yaml.load(rule1)); rules.add(yaml.load(rule2)); diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java index 40bef70dfb..9ca725f8f7 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java @@ -20,7 +20,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; +import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; @@ -107,7 +107,7 @@ class AbstractClusterInvokerTest { @SuppressWarnings({"unchecked"}) @BeforeEach public void setUp() throws Exception { - ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class); + ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); Map attributes = new HashMap<>(); attributes.put("application", "abstractClusterInvokerTest"); url = url.putAttribute(REFER_KEY, attributes); diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ConnectivityValidationTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ConnectivityValidationTest.java index 59fc9cfd10..23cb42831f 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ConnectivityValidationTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ConnectivityValidationTest.java @@ -19,7 +19,7 @@ package org.apache.dubbo.rpc.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.CollectionUtils; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; +import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; @@ -72,7 +72,7 @@ class ConnectivityValidationTest { @BeforeEach public void setup() { - ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class); + ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); invoker1 = Mockito.mock(Invoker.class); invoker2 = Mockito.mock(Invoker.class); invoker3 = Mockito.mock(Invoker.class); diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java index de20e2d262..4c99c50b35 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java @@ -17,7 +17,7 @@ package org.apache.dubbo.rpc.cluster.support; import org.apache.dubbo.common.URL; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; +import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; @@ -67,7 +67,7 @@ class FailoverClusterInvokerTest { @BeforeEach public void setUp() throws Exception { - ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class); + ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); dic = mock(Directory.class); given(dic.getUrl()).willReturn(url); diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java index 7db7129e81..b15c87324f 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java @@ -18,7 +18,7 @@ package org.apache.dubbo.rpc.cluster.support.wrapper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; +import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; @@ -51,7 +51,7 @@ class MockClusterInvokerTest { @BeforeEach public void beforeMethod() { - ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class); + ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); invokers.clear(); } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockProviderRpcExceptionTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockProviderRpcExceptionTest.java index c0a737280f..e692aae745 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockProviderRpcExceptionTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockProviderRpcExceptionTest.java @@ -18,7 +18,7 @@ package org.apache.dubbo.rpc.cluster.support.wrapper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; +import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.ProxyFactory; @@ -48,7 +48,7 @@ class MockProviderRpcExceptionTest { @BeforeEach public void beforeMethod() { - ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class); + ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); invokers.clear(); } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/ScopeClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/ScopeClusterInvokerTest.java index 889b3ab7d7..44132df55c 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/ScopeClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/ScopeClusterInvokerTest.java @@ -20,13 +20,14 @@ package org.apache.dubbo.rpc.cluster.support.wrapper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; +import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.LoadBalance; @@ -41,7 +42,9 @@ import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.common.constants.CommonConstants.EXPORTER_LISTENER_KEY; @@ -50,6 +53,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.rpc.Constants.LOCAL_PROTOCOL; import static org.apache.dubbo.rpc.Constants.SCOPE_LOCAL; +import static org.apache.dubbo.rpc.cluster.Constants.PEER_KEY; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; import static org.apache.dubbo.rpc.cluster.Constants.SCOPE_KEY; @@ -64,7 +68,7 @@ class ScopeClusterInvokerTest { @BeforeEach void beforeMonth() { - ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class); + ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); } @AfterEach @@ -75,6 +79,9 @@ class ScopeClusterInvokerTest { exporters.clear(); for (Invoker invoker : invokers) { invoker.destroy(); + if (invoker instanceof ScopeClusterInvoker) { + Assertions.assertTrue(((ScopeClusterInvoker) invoker).isDestroyed()); + } } invokers.clear(); } @@ -104,7 +111,7 @@ class ScopeClusterInvokerTest { url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); URL injvmUrl = URL.valueOf("injvm://127.0.0.1/TestService") - .addParameter(INTERFACE_KEY, DemoService.class.getName()).setScopeModel(ApplicationModel.defaultModel().getDefaultModule()) ; + .addParameter(INTERFACE_KEY, DemoService.class.getName()).setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); Exporter exporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, injvmUrl)); exporters.add(exporter); @@ -152,12 +159,6 @@ class ScopeClusterInvokerTest { url = url.addParameter(SCOPE_KEY, SCOPE_LOCAL); url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); - URL injvmUrl = URL.valueOf("injvm://127.0.0.1/TestService") - .addParameter(INTERFACE_KEY, DemoService.class.getName()); - injvmUrl = injvmUrl.addParameter(EXPORTER_LISTENER_KEY, LOCAL_PROTOCOL).setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); - Exporter exporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, injvmUrl)); - exporters.add(exporter); - Invoker cluster = getClusterInvoker(url); invokers.add(cluster); @@ -165,8 +166,22 @@ class ScopeClusterInvokerTest { invocation.setMethodName("doSomething4"); invocation.setParameterTypes(new Class[]{}); Assertions.assertTrue(cluster.isAvailable(), ""); - Result ret = cluster.invoke(invocation); - Assertions.assertEquals("doSomething4", ret.getValue()); + RpcInvocation finalInvocation = invocation; + Assertions.assertThrows(RpcException.class, () -> cluster.invoke(finalInvocation)); + + URL injvmUrl = URL.valueOf("injvm://127.0.0.1/TestService") + .addParameter(INTERFACE_KEY, DemoService.class.getName()); + injvmUrl = injvmUrl.addParameter(EXPORTER_LISTENER_KEY, LOCAL_PROTOCOL).setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); + Exporter exporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, injvmUrl)); + exporters.add(exporter); + + + invocation = new RpcInvocation(); + invocation.setMethodName("doSomething4"); + invocation.setParameterTypes(new Class[]{}); + Assertions.assertTrue(cluster.isAvailable(), ""); + Result ret2 = cluster.invoke(invocation); + Assertions.assertEquals("doSomething4", ret2.getValue()); } @Test @@ -184,13 +199,12 @@ class ScopeClusterInvokerTest { Invoker cluster = getClusterInvoker(url); invokers.add(cluster); - //Configured with mock RpcInvocation invocation = new RpcInvocation(); - invocation.setMethodName("doSomething4"); + invocation.setMethodName("doSomething5"); invocation.setParameterTypes(new Class[]{}); Assertions.assertTrue(cluster.isAvailable(), ""); Result ret = cluster.invoke(invocation); - Assertions.assertEquals("doSomething4", ret.getValue()); + Assertions.assertEquals("doSomething5", ret.getValue()); } @Test @@ -221,6 +235,91 @@ class ScopeClusterInvokerTest { Assertions.assertFalse(((AtomicBoolean) isExported.get(cluster)).get()); } + @Test + void testPeerInvoke() { + URL url = URL.valueOf("remote://1.2.3.4/" + DemoService.class.getName()); + url = url.addParameter(REFER_KEY, + URL.encode(PATH_KEY + "=" + DemoService.class.getName())); + Map peer = new HashMap<>(); + peer.put(PEER_KEY, true); + url = url.addAttributes(peer); + url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); + + Invoker cluster = getClusterInvoker(url); + invokers.add(cluster); + + //Configured with mock + RpcInvocation invocation = new RpcInvocation(); + invocation.setMethodName("doSomething6"); + invocation.setParameterTypes(new Class[]{}); + Result ret = cluster.invoke(invocation); + Assertions.assertEquals("doSomething6", ret.getValue()); + + } + + @Test + void testInjvmUrlInvoke() { + URL url = URL.valueOf("injvm://1.2.3.4/" + DemoService.class.getName()); + url = url.addParameter(REFER_KEY, + URL.encode(PATH_KEY + "=" + DemoService.class.getName())); + url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); + + Invoker cluster = getClusterInvoker(url); + invokers.add(cluster); + + //Configured with mock + RpcInvocation invocation = new RpcInvocation(); + invocation.setMethodName("doSomething7"); + invocation.setParameterTypes(new Class[]{}); + Result ret = cluster.invoke(invocation); + Assertions.assertEquals("doSomething7", ret.getValue()); + + } + + @Test + void testDynamicInvoke() { + URL url = URL.valueOf("remote://1.2.3.4/" + DemoService.class.getName()); + url = url.addParameter(REFER_KEY, + URL.encode(PATH_KEY + "=" + DemoService.class.getName())); + url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); + + Invoker cluster = getClusterInvoker(url); + invokers.add(cluster); + + RpcInvocation invocation = new RpcInvocation(); + invocation.setMethodName("doSomething8"); + invocation.setParameterTypes(new Class[]{}); + Result ret1 = cluster.invoke(invocation); + Assertions.assertEquals("doSomething8", ret1.getValue()); + + RpcContext.getServiceContext().setLocalInvoke(true); + + invocation = new RpcInvocation(); + invocation.setMethodName("doSomething8"); + invocation.setParameterTypes(new Class[]{}); + RpcInvocation finalInvocation = invocation; + Assertions.assertThrows(RpcException.class, () -> cluster.invoke(finalInvocation)); + + URL injvmUrl = URL.valueOf("injvm://127.0.0.1/TestService") + .addParameter(INTERFACE_KEY, DemoService.class.getName()).setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); + Exporter exporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, injvmUrl)); + exporters.add(exporter); + + invocation = new RpcInvocation(); + invocation.setMethodName("doSomething8"); + invocation.setParameterTypes(new Class[]{}); + Result ret2 = cluster.invoke(invocation); + Assertions.assertEquals("doSomething8", ret2.getValue()); + + RpcContext.getServiceContext().setLocalInvoke(false); + + invocation = new RpcInvocation(); + invocation.setMethodName("doSomething8"); + invocation.setParameterTypes(new Class[]{}); + Result ret3 = cluster.invoke(invocation); + Assertions.assertEquals("doSomething8", ret3.getValue()); + } + private Invoker getClusterInvoker(URL url) { final URL durl = url.addParameter("proxy", "jdk"); invokers.clear(); @@ -241,7 +340,10 @@ class ScopeClusterInvokerTest { } } }; - return new ScopeClusterInvoker<>(dic, cluster); + ScopeClusterInvoker demoServiceScopeClusterInvoker = new ScopeClusterInvoker<>(dic, cluster); + Assertions.assertNotNull(demoServiceScopeClusterInvoker.getDirectory()); + Assertions.assertNotNull(demoServiceScopeClusterInvoker.getInvoker()); + return demoServiceScopeClusterInvoker; } public static interface DemoService { @@ -252,6 +354,14 @@ class ScopeClusterInvokerTest { String doSomething3(); String doSomething4(); + + String doSomething5(); + + String doSomething6(); + + String doSomething7(); + + String doSomething8(); } public static class DemoServiceImpl implements DemoService { @@ -276,6 +386,26 @@ class ScopeClusterInvokerTest { public String doSomething4() { return "doSomething4"; } + + @Override + public String doSomething5() { + return "doSomething5"; + } + + @Override + public String doSomething6() { + return "doSomething6"; + } + + @Override + public String doSomething7() { + return "doSomething7"; + } + + @Override + public String doSomething8() { + return "doSomething8"; + } } } diff --git a/dubbo-common/pom.xml b/dubbo-common/pom.xml index e05a2634bd..d9413ee7ad 100644 --- a/dubbo-common/pom.xml +++ b/dubbo-common/pom.xml @@ -101,37 +101,4 @@ javax.annotation-api - - - - - org.apache.maven.plugins - maven-antrun-plugin - - - get-version-infos - compile - - true - - - - - - - - - - - - - run - - - - - - diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/BatchExecutorQueue.java b/dubbo-common/src/main/java/org/apache/dubbo/common/BatchExecutorQueue.java index 4718f21feb..c958f56508 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/BatchExecutorQueue.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/BatchExecutorQueue.java @@ -62,7 +62,7 @@ public class BatchExecutorQueue { boolean flushedOnce = false; while ((item = snapshot.poll()) != null) { if (snapshot.size() == 0) { - i = 0; + flushedOnce = false; break; } if (i == chunkSize) { @@ -74,7 +74,7 @@ public class BatchExecutorQueue { i++; } } - if ((i != 0 || !flushedOnce) && item != null) { + if (!flushedOnce && item != null) { flush(item); } } finally { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java b/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java index 6b5bb4f279..ec317703a5 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java @@ -18,20 +18,18 @@ package org.apache.dubbo.common; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.common.utils.ClassUtils; -import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.StringUtils; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.security.CodeSource; -import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import java.util.Optional; -import java.util.Properties; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -64,25 +62,38 @@ public final class Version { private static final Map VERSION2INT = new HashMap(); static { - // check if there's duplicated jar - Version.checkDuplicate(Version.class); - // get dubbo version and last commit id try { - Properties properties = - ConfigUtils.loadProperties(Collections.emptySet(), "META-INF/version"); - - VERSION = Optional.ofNullable(properties.getProperty("revision")) - .filter(StringUtils::isNotBlank) - .orElseGet(() -> getVersion(Version.class, "")); - LATEST_COMMIT_ID = Optional.ofNullable(properties.getProperty("git.commit.id")).orElse(""); + tryLoadVersionFromResource(); + checkDuplicate(); } catch (Throwable e) { logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", "continue the old logic, ignore exception " + e.getMessage(), e); + } + if (StringUtils.isEmpty(VERSION)) { VERSION = getVersion(Version.class, ""); + } + if (StringUtils.isEmpty(LATEST_COMMIT_ID)) { LATEST_COMMIT_ID = ""; } } + private static void tryLoadVersionFromResource() throws IOException { + Enumeration configLoader = Version.class.getClassLoader().getResources("META-INF/versions/dubbo-common"); + if (configLoader.hasMoreElements()) { + URL url = configLoader.nextElement(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.startsWith("revision=")) { + VERSION = line.substring("revision=".length()); + } else if (line.startsWith("git.commit.id=")) { + LATEST_COMMIT_ID = line.substring("git.commit.id=".length()); + } + } + } + } + } + private Version() { } @@ -266,48 +277,75 @@ public final class Version { return file; } - public static void checkDuplicate(Class cls, boolean failOnError) { - checkDuplicate(cls.getName().replace('.', '/') + ".class", failOnError); - } - - public static void checkDuplicate(Class cls) { - checkDuplicate(cls, false); - } - - public static void checkDuplicate(String path, boolean failOnError) { + private static void checkDuplicate() { try { - // search in caller's classloader - Set files = getResources(path); - // duplicated jar is found - if (files.size() > 1) { - String error = "Duplicate class " + path + " in " + files.size() + " jar " + files; - if (failOnError) { - throw new IllegalStateException(error); - } else { - logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", error); - } - } + checkArtifacts(loadArtifactIds()); } catch (Throwable e) { logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", e.getMessage(), e); } } - /** - * search resources in caller's classloader - */ - private static Set getResources(String path) throws IOException { - Enumeration urls = ClassUtils.getCallerClassLoader(Version.class).getResources(path); - Set files = new HashSet(); - while (urls.hasMoreElements()) { - URL url = urls.nextElement(); - if (url != null) { - String file = url.getFile(); - if (StringUtils.isNotEmpty(file)) { - files.add(file); + private static void checkArtifacts(Set artifactIds) throws IOException { + if (!artifactIds.isEmpty()) { + for (String artifactId : artifactIds) { + checkArtifact(artifactId); + } + } + } + + private static void checkArtifact(String artifactId) throws IOException { + Enumeration artifactEnumeration = Version.class.getClassLoader().getResources("META-INF/versions/" + artifactId); + while (artifactEnumeration.hasMoreElements()) { + URL url = artifactEnumeration.nextElement(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.startsWith("#")) { + continue; + } + String[] artifactInfo = line.split("="); + if (artifactInfo.length == 2) { + String key = artifactInfo[0]; + String value = artifactInfo[1]; + checkVersion(artifactId, url, key, value); + } } } } - return files; + } + + private static void checkVersion(String artifactId, URL url, String key, String value) { + if ("revision".equals(key) && !value.equals(VERSION)) { + String error = "Inconsistent version " + value + " found in " + artifactId + " from " + url.getPath() + ", " + + "expected dubbo-common version is " + VERSION; + logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", error); + } + if ("git.commit.id".equals(key) && !value.equals(LATEST_COMMIT_ID)) { + String error = "Inconsistent git build commit id " + value + " found in " + artifactId + " from " + url.getPath() + ", " + + "expected dubbo-common version is " + LATEST_COMMIT_ID; + logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", error); + } + } + + private static Set loadArtifactIds() throws IOException { + Enumeration artifactsEnumeration = Version.class.getClassLoader().getResources("META-INF/versions/.artifacts"); + Set artifactIds = new HashSet<>(); + while (artifactsEnumeration.hasMoreElements()) { + URL url = artifactsEnumeration.nextElement(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.startsWith("#")) { + continue; + } + if (StringUtils.isEmpty(line)) { + continue; + } + artifactIds.add(line); + } + } + } + return artifactIds; } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/beans/factory/ScopeBeanFactory.java b/dubbo-common/src/main/java/org/apache/dubbo/common/beans/factory/ScopeBeanFactory.java index c2b52897ca..9f8dfbe779 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/beans/factory/ScopeBeanFactory.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/beans/factory/ScopeBeanFactory.java @@ -268,6 +268,10 @@ public class ScopeBeanFactory { } } + public boolean isDestroyed() { + return destroyed.get(); + } + private void checkDestroyed() { if (destroyed.get()) { throw new IllegalStateException("ScopeBeanFactory is destroyed"); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java index 62ff4beeeb..625ddfb2f8 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java @@ -118,8 +118,6 @@ public interface CommonConstants { String CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY = "CONSUMER_SHARED_SERVICE_EXECUTOR"; - String INTERNAL_EXECUTOR_SERVICE_COMPONENT_KEY = "INTERNAL_SERVICE_EXECUTOR"; - String THREADPOOL_KEY = "threadpool"; String THREAD_NAME_KEY = "threadname"; @@ -620,5 +618,14 @@ public interface CommonConstants { String ENCODE_IN_IO_THREAD_KEY = "encode.in.io"; boolean DEFAULT_ENCODE_IN_IO_THREAD = false; + /** + * @since 3.2.0 + */ + String BYTE_ACCESSOR_KEY = "byte.accessor"; + String PAYLOAD = "payload"; + + String DUBBO_METRICS_CONFIGCENTER_ENABLE = "dubbo.metrics.configcenter.enable"; + + Integer TRI_EXCEPTION_CODE_NOT_EXISTS = 0; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java index c39fa1c698..568a5d888d 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java @@ -196,6 +196,8 @@ public interface LoggerCodeConstants { String REGISTRY_ISTIO_EXCEPTION = "1-41"; + String REGISTRY_NACOS_SUB_LEGACY = "1-42"; + // Cluster module 2-x String CLUSTER_FAILED_SITE_SELECTION = "2-1"; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java index 89e0333b36..afd0bedfe5 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java @@ -37,8 +37,14 @@ public interface MetricsConstants { String TAG_VERSION_KEY = "version"; String TAG_APPLICATION_VERSION_KEY = "application.version"; + + String TAG_KEY_KEY = "key"; + + String TAG_CONFIG_CENTER = "config.center"; + + String TAG_CHANGE_TYPE = "change.type"; - String ENABLE_JVM_METRICS_KEY = "enable.jvm.metrics"; + String ENABLE_JVM_METRICS_KEY = "enable.jvm"; String AGGREGATION_COLLECTOR_KEY = "aggregation"; @@ -48,6 +54,8 @@ public interface MetricsConstants { String AGGREGATION_TIME_WINDOW_SECONDS_KEY = "aggregation.time.window.seconds"; + String HISTOGRAM_ENABLED_KEY = "histogram.enabled"; + String PROMETHEUS_EXPORTER_ENABLED_KEY = "prometheus.exporter.enabled"; String PROMETHEUS_EXPORTER_ENABLE_HTTP_SERVICE_DISCOVERY_KEY = "prometheus.exporter.enable.http.service.discovery"; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ApplicationDeployListener.java b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ApplicationDeployListener.java index 674ca90e3c..f045c4259d 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ApplicationDeployListener.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ApplicationDeployListener.java @@ -25,6 +25,7 @@ import org.apache.dubbo.rpc.model.ApplicationModel; */ @SPI(scope = ExtensionScope.APPLICATION) public interface ApplicationDeployListener extends DeployListener { + default void onModuleStarted(ApplicationModel applicationModel) { } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListener.java b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListener.java index fb0e7ad4d9..94d31bbd64 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListener.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListener.java @@ -19,6 +19,10 @@ package org.apache.dubbo.common.deploy; import org.apache.dubbo.rpc.model.ScopeModel; public interface DeployListener { + /** + * Useful to inject some configuration like MetricsConfig, RegistryConfig, etc. + */ + void onInitialize(E scopeModel); void onStarting(E scopeModel); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListenerAdapter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListenerAdapter.java index ed78f40c8d..925747dae4 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListenerAdapter.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListenerAdapter.java @@ -19,6 +19,11 @@ package org.apache.dubbo.common.deploy; import org.apache.dubbo.rpc.model.ScopeModel; public class DeployListenerAdapter implements DeployListener{ + @Override + public void onInitialize(E scopeModel) { + + } + @Override public void onStarting(E scopeModel) { } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/JacksonImpl.java b/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/JacksonImpl.java index dc6f6e380b..6fbd398a01 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/JacksonImpl.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/JacksonImpl.java @@ -27,7 +27,7 @@ import java.lang.reflect.Type; import java.util.List; public class JacksonImpl extends AbstractJSONImpl { - private ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectMapper objectMapper = new ObjectMapper(); private volatile Object jacksonCache = null; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/status/reporter/FrameworkStatusReportService.java b/dubbo-common/src/main/java/org/apache/dubbo/common/status/reporter/FrameworkStatusReportService.java index ef0a74959b..8c8c65a8d3 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/status/reporter/FrameworkStatusReportService.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/status/reporter/FrameworkStatusReportService.java @@ -75,7 +75,7 @@ public class FrameworkStatusReportService implements ScopeModelAware { HashMap registration = new HashMap<>(); registration.put("application", applicationModel.getApplicationName()); registration.put("status", status); - return JsonUtils.getJson().toJson(registration); + return JsonUtils.toJson(registration); } public String createConsumptionReport(String interfaceName, String version, String group, String status) { @@ -86,7 +86,7 @@ public class FrameworkStatusReportService implements ScopeModelAware { migrationStatus.put("version", version); migrationStatus.put("group", group); migrationStatus.put("status", status); - return JsonUtils.getJson().toJson(migrationStatus); + return JsonUtils.toJson(migrationStatus); } public String createMigrationStepReport(String interfaceName, String version, String group, String originStep, String newStep, String success) { @@ -99,6 +99,6 @@ public class FrameworkStatusReportService implements ScopeModelAware { migrationStatus.put("originStep", originStep); migrationStatus.put("newStep", newStep); migrationStatus.put("success", success); - return JsonUtils.getJson().toJson(migrationStatus); + return JsonUtils.toJson(migrationStatus); } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java index f1479e60ae..9e127af51e 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java @@ -21,11 +21,12 @@ import org.apache.dubbo.common.logger.LoggerFactory; import java.util.Collections; import java.util.List; +import java.util.Queue; import java.util.concurrent.AbstractExecutorService; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.LockSupport; /** * The most important difference between this Executor and other normal Executor is that this one doesn't manage @@ -38,78 +39,42 @@ import java.util.concurrent.TimeUnit; public class ThreadlessExecutor extends AbstractExecutorService { private static final Logger logger = LoggerFactory.getLogger(ThreadlessExecutor.class.getName()); - private final BlockingQueue queue = new LinkedBlockingQueue<>(); + private static final Object SHUTDOWN = new Object(); - private CompletableFuture waitingFuture; + private final Queue queue = new ConcurrentLinkedQueue<>(); - private boolean finished = false; - - private volatile boolean waiting = true; - - private final Object lock = new Object(); - - public CompletableFuture getWaitingFuture() { - return waitingFuture; - } - - public void setWaitingFuture(CompletableFuture waitingFuture) { - this.waitingFuture = waitingFuture; - } - - private boolean isFinished() { - return finished; - } - - private void setFinished(boolean finished) { - this.finished = finished; - } - - public boolean isWaiting() { - return waiting; - } - - private void setWaiting(boolean waiting) { - this.waiting = waiting; - } + /** + * Wait thread. It must be visible to other threads and does not need to be thread-safe + */ + private volatile Object waiter; /** * Waits until there is a task, executes the task and all queued tasks (if there're any). The task is either a normal * response or a timeout response. */ public void waitAndDrain() throws InterruptedException { - /** - * Usually, {@link #waitAndDrain()} will only get called once. It blocks for the response for the first time, - * once the response (the task) reached and being executed waitAndDrain will return, the whole request process - * then finishes. Subsequent calls on {@link #waitAndDrain()} (if there're any) should return immediately. - * - * There's no need to worry that {@link #finished} is not thread-safe. Checking and updating of - * 'finished' only appear in waitAndDrain, since waitAndDrain is binding to one RPC call (one thread), the call - * of it is totally sequential. - */ - if (isFinished()) { - return; + throwIfInterrupted(); + Runnable runnable = queue.poll(); + if (runnable == null) { + waiter = Thread.currentThread(); + try { + while ((runnable = queue.poll()) == null) { + LockSupport.park(this); + throwIfInterrupted(); + } + } finally { + waiter = null; + } } - - Runnable runnable; - try { - runnable = queue.take(); - } catch (InterruptedException e) { - setWaiting(false); - throw e; - } - - synchronized (lock) { - setWaiting(false); + do { runnable.run(); - } + } while ((runnable = queue.poll()) != null); + } - runnable = queue.poll(); - while (runnable != null) { - runnable.run(); - runnable = queue.poll(); + private static void throwIfInterrupted() throws InterruptedException { + if (Thread.interrupted()) { + throw new InterruptedException(); } - // mark the status of ThreadlessExecutor as finished. - setFinished(true); } /** @@ -120,26 +85,15 @@ public class ThreadlessExecutor extends AbstractExecutorService { */ @Override public void execute(Runnable runnable) { - runnable = new RunnableWrapper(runnable); - synchronized (lock) { - if (!isWaiting()) { - runnable.run(); - return; - } - queue.add(runnable); + RunnableWrapper run = new RunnableWrapper(runnable); + queue.add(run); + if (waiter != SHUTDOWN) { + LockSupport.unpark((Thread) waiter); + } else if (queue.remove(run)) { + throw new RejectedExecutionException(); } } - /** - * tells the thread blocking on {@link #waitAndDrain()} to return, despite of the current status, to avoid endless waiting. - */ - public void notifyReturn(Throwable t) { - // an empty runnable task. - execute(() -> { - waitingFuture.completeExceptionally(t); - }); - } - /** * The following methods are still not supported */ @@ -151,23 +105,26 @@ public class ThreadlessExecutor extends AbstractExecutorService { @Override public List shutdownNow() { - notifyReturn(new IllegalStateException("Consumer is shutting down and this call is going to be stopped without " + - "receiving any result, usually this is called by a slow provider instance or bad service implementation.")); + waiter = SHUTDOWN; + Runnable runnable; + while ((runnable = queue.poll()) != null) { + runnable.run(); + } return Collections.emptyList(); } @Override public boolean isShutdown() { - return false; + return waiter == SHUTDOWN; } @Override public boolean isTerminated() { - return false; + return isShutdown(); } @Override - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + public boolean awaitTermination(long timeout, TimeUnit unit) { return false; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java index e35617e44c..ae1c9aaacc 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java @@ -30,11 +30,13 @@ import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.ProviderConfig; -import org.apache.dubbo.rpc.model.ApplicationModel; -import org.apache.dubbo.rpc.model.ModuleModel; -import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.executor.DefaultExecutorSupport; import org.apache.dubbo.rpc.executor.ExecutorSupport; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ConsumerModel; +import org.apache.dubbo.rpc.model.ModuleModel; +import org.apache.dubbo.rpc.model.ProviderModel; +import org.apache.dubbo.rpc.model.ServiceModel; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -50,7 +52,6 @@ import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_EXPORT_T import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_REFER_THREAD_NUM; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_SERVICE_COMPONENT_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.INTERNAL_EXECUTOR_SERVICE_COMPONENT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; @@ -64,6 +65,8 @@ import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXP public class DefaultExecutorRepository implements ExecutorRepository, ExtensionAccessorAware { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultExecutorRepository.class); + private static final String MAX_KEY = String.valueOf(Integer.MAX_VALUE); + private volatile ScheduledExecutorService serviceExportExecutor; private volatile ExecutorService serviceReferExecutor; @@ -131,16 +134,34 @@ public class DefaultExecutorRepository implements ExecutorRepository, ExtensionA } } + private String getExecutorSecondKey(ServiceModel serviceModel, URL url) { + if (serviceModel instanceof ConsumerModel) { + return getConsumerKey(serviceModel); + } else { + return getProviderKey((ProviderModel) serviceModel, url); + } + } + private String getConsumerKey(URL url) { // Consumer's executor is sharing globally, key=Integer.MAX_VALUE return String.valueOf(Integer.MAX_VALUE); } + private String getConsumerKey(ServiceModel serviceModel) { + // Consumer's executor is sharing globally, key=Integer.MAX_VALUE + return MAX_KEY; + } + protected String getProviderKey(URL url) { // Provider's executor is sharing by protocol. return String.valueOf(url.getPort()); } + protected String getProviderKey(ProviderModel providerModel, URL url) { + // Provider's executor is sharing by protocol. + return String.valueOf(url.getPort()); + } + /** * Return the executor key based on the type (internal or biz) of the current service. * @@ -148,18 +169,18 @@ public class DefaultExecutorRepository implements ExecutorRepository, ExtensionA * @return */ private String getExecutorKey(URL url) { - String executorKey = INTERNAL_EXECUTOR_SERVICE_COMPONENT_KEY; - ServiceDescriptor serviceDescriptor = applicationModel.getInternalModule().getServiceRepository().lookupService(url.getServiceInterface()); - // if not found in internal service repository, then it's biz service defined by user. - if (serviceDescriptor == null) { - executorKey = EXECUTOR_SERVICE_COMPONENT_KEY; - - } - if (CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY))) { - executorKey = CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY; + return CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY; + } + return EXECUTOR_SERVICE_COMPONENT_KEY; + } + + private String getExecutorKey(ServiceModel serviceModel) { + if (serviceModel instanceof ProviderModel) { + return EXECUTOR_SERVICE_COMPONENT_KEY; + } else { + return CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY; } - return executorKey; } protected ExecutorService createExecutor(URL url) { @@ -197,6 +218,37 @@ public class DefaultExecutorRepository implements ExecutorRepository, ExtensionA } } + @Override + public ExecutorService getExecutor(ServiceModel serviceModel, URL url) { + Map executors = data.get(getExecutorKey(serviceModel)); + + /* + * It's guaranteed that this method is called after {@link #createExecutorIfAbsent(URL)}, so data should already + * have Executor instances generated and stored. + */ + if (executors == null) { + logger.warn(COMMON_EXECUTORS_NO_FOUND, "", "", "No available executors, this is not expected, framework should call createExecutorIfAbsent first" + + "before coming to here."); + + return null; + } + + // Consumer's executor is sharing globally, key=Integer.MAX_VALUE. Provider's executor is sharing by protocol. + String executorCacheKey = getExecutorSecondKey(serviceModel, url); + ExecutorService executor = executors.get(executorCacheKey); + if (executor != null && (executor.isShutdown() || executor.isTerminated())) { + executors.remove(executorCacheKey); + // Does not re-create a shutdown executor, use SHARED_EXECUTOR for downgrade. + executor = null; + logger.info("Executor for " + url + " is shutdown."); + } + if (executor == null) { + return frameworkExecutorRepository.getSharedExecutor(); + } else { + return executor; + } + } + @Override public void updateThreadpool(URL url, ExecutorService executor) { try { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java index e9e13b77c7..67ab0cf2ab 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java @@ -22,19 +22,20 @@ import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ApplicationConfig; -import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.executor.ExecutorSupport; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ServiceModel; import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; -import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_ISOLATION; /** * */ -@SPI(value = "default", scope = ExtensionScope.APPLICATION) +@SPI(value = "isolation", scope = ExtensionScope.APPLICATION) public interface ExecutorRepository { /** @@ -54,6 +55,8 @@ public interface ExecutorRepository { */ ExecutorService getExecutor(URL url); + ExecutorService getExecutor(ServiceModel serviceModel, URL url); + /** @@ -187,7 +190,7 @@ public interface ExecutorRepository { static String getMode(ApplicationModel applicationModel) { Optional optional = applicationModel.getApplicationConfigManager().getApplication(); - return optional.map(ApplicationConfig::getExecutorManagementMode).orElse(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + return optional.map(ApplicationConfig::getExecutorManagementMode).orElse(EXECUTOR_MANAGEMENT_MODE_ISOLATION); } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/FrameworkExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/FrameworkExecutorRepository.java index ce70b83fa4..f0cf0bc618 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/FrameworkExecutorRepository.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/FrameworkExecutorRepository.java @@ -27,6 +27,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -56,6 +57,8 @@ public class FrameworkExecutorRepository implements Disposable { private final Ring executorServiceRing = new Ring<>(); + private final ExecutorService internalServiceExecutor; + public FrameworkExecutorRepository() { sharedExecutor = Executors.newCachedThreadPool(new NamedThreadFactory("Dubbo-framework-shared-handler", true)); sharedScheduledExecutor = Executors.newScheduledThreadPool(8, new NamedThreadFactory("Dubbo-framework-shared-scheduler", true)); @@ -89,6 +92,9 @@ public class FrameworkExecutorRepository implements Disposable { } metadataRetryExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-framework-metadata-retry")); + internalServiceExecutor = new ThreadPoolExecutor(0, 100, 60L, TimeUnit.SECONDS, + new SynchronousQueue<>(), new NamedInternalThreadFactory("Dubbo-internal-service", true), + new ThreadPoolExecutor.AbortPolicy()); } /** @@ -122,6 +128,10 @@ public class FrameworkExecutorRepository implements Disposable { return metadataRetryExecutor; } + public ExecutorService getInternalServiceExecutor() { + return internalServiceExecutor; + } + /** * Get the default shared thread pool. * @@ -177,6 +187,7 @@ public class FrameworkExecutorRepository implements Disposable { logger.info("destroying framework executor repository .."); shutdownExecutorService(poolRouterExecutor, "poolRouterExecutor"); shutdownExecutorService(metadataRetryExecutor, "metadataRetryExecutor"); + shutdownExecutorService(internalServiceExecutor, "internalServiceExecutor"); // scheduledExecutors shutdownExecutorServices(scheduledExecutors.listItems(), "scheduledExecutors"); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/IsolationExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/IsolationExecutorRepository.java index d507992071..0b6d66fe3e 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/IsolationExecutorRepository.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/IsolationExecutorRepository.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.executor.ExecutorSupport; import org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory; import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ProviderModel; import java.util.concurrent.ExecutorService; @@ -52,6 +53,15 @@ public class IsolationExecutorRepository extends DefaultExecutorRepository { } } + @Override + protected String getProviderKey(ProviderModel providerModel, URL url) { + if (url.getAttributes().containsKey(SERVICE_EXECUTOR)) { + return providerModel.getServiceKey(); + } else { + return super.getProviderKey(url); + } + } + @Override protected ExecutorService createExecutor(URL url) { Object executor = url.getAttributes().get(SERVICE_EXECUTOR); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java b/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java index b6589949d4..a5c255b2b8 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java @@ -422,7 +422,7 @@ public class HashedWheelTimer implements Timer { private static void reportTooManyInstances() { String resourceType = ClassUtils.simpleClassName(HashedWheelTimer.class); logger.error(COMMON_ERROR_TOO_MANY_INSTANCES, "", "", "You are creating too many " + resourceType + " instances. " + - resourceType + " is a shared resource that must be reused across the JVM," + + resourceType + " is a shared resource that must be reused across the JVM, " + "so that only a few instances are created."); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java index 243e1d42ca..cf6ba67510 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java @@ -994,7 +994,13 @@ public class URLParam { Map> methodParameters = new HashMap<>(capacity); for (Map.Entry entry : params.entrySet()) { - addParameter(keyBit, valueMap, extraParam, methodParameters, entry.getKey(), entry.getValue(), false); + String key = entry.getKey(); + String value = entry.getValue(); + addParameter(keyBit, valueMap, extraParam, methodParameters, key, value, false); + // compatible with lower versions registering "default." keys + if (key.startsWith(DEFAULT_KEY_PREFIX)) { + addParameter(keyBit, valueMap, extraParam, methodParameters, key.substring(DEFAULT_KEY_PREFIX.length()), value, true); + } } return new URLParam(keyBit, valueMap, extraParam, methodParameters, rawParam); } else { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassUtils.java index a403519318..d07d01f3a5 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassUtils.java @@ -74,20 +74,20 @@ public class ClassUtils { * @since 2.7.6 */ public static final Set> SIMPLE_TYPES = ofSet( - Void.class, - Boolean.class, - Character.class, - Byte.class, - Short.class, - Integer.class, - Long.class, - Float.class, - Double.class, - String.class, - BigDecimal.class, - BigInteger.class, - Date.class, - Object.class + Void.class, + Boolean.class, + Character.class, + Byte.class, + Short.class, + Integer.class, + Long.class, + Float.class, + Double.class, + String.class, + BigDecimal.class, + BigInteger.class, + Date.class, + Object.class ); /** * Prefix for internal array class names: "[L" @@ -118,8 +118,8 @@ public class ClassUtils { Set> primitiveTypeNames = new HashSet<>(32); primitiveTypeNames.addAll(PRIMITIVE_WRAPPER_TYPE_MAP.values()); primitiveTypeNames.addAll(Arrays - .asList(boolean[].class, byte[].class, char[].class, double[].class, - float[].class, int[].class, long[].class, short[].class)); + .asList(boolean[].class, byte[].class, char[].class, double[].class, + float[].class, int[].class, long[].class, short[].class)); for (Class primitiveTypeName : primitiveTypeNames) { PRIMITIVE_TYPE_NAME_MAP.put(primitiveTypeName.getName(), primitiveTypeName); } @@ -137,12 +137,12 @@ public class ClassUtils { private static final char PACKAGE_SEPARATOR_CHAR = '.'; public static Class forNameWithThreadContextClassLoader(String name) - throws ClassNotFoundException { + throws ClassNotFoundException { return forName(name, Thread.currentThread().getContextClassLoader()); } public static Class forNameWithCallerClassLoader(String name, Class caller) - throws ClassNotFoundException { + throws ClassNotFoundException { return forName(name, caller.getClassLoader()); } @@ -224,7 +224,7 @@ public class ClassUtils { * @see Class#forName(String, boolean, ClassLoader) */ public static Class forName(String name, ClassLoader classLoader) - throws ClassNotFoundException, LinkageError { + throws ClassNotFoundException, LinkageError { Class clazz = resolvePrimitiveClassName(name); if (clazz != null) { @@ -244,7 +244,7 @@ public class ClassUtils { String elementClassName = null; if (internalArrayMarker == 0) { elementClassName = name - .substring(INTERNAL_ARRAY_PREFIX.length(), name.length() - 1); + .substring(INTERNAL_ARRAY_PREFIX.length(), name.length() - 1); } else if (name.startsWith("[")) { elementClassName = name.substring(1); } @@ -354,7 +354,7 @@ public class ClassUtils { */ public static boolean isTypeMatch(Class type, String value) { if ((type == boolean.class || type == Boolean.class) - && !("true".equals(value) || "false".equals(value))) { + && !("true".equals(value) || "false".equals(value))) { return false; } return true; @@ -408,18 +408,18 @@ public class ClassUtils { if (isNotEmpty(interfaces)) { // add current interfaces Arrays.stream(interfaces) - .filter(resolved::add) - .forEach(cls -> { - allInterfaces.add(cls); - waitResolve.add(cls); - }); + .filter(resolved::add) + .forEach(cls -> { + allInterfaces.add(cls); + waitResolve.add(cls); + }); } // add all super classes to waitResolve getAllSuperClasses(clazz) - .stream() - .filter(resolved::add) - .forEach(waitResolve::add); + .stream() + .filter(resolved::add) + .forEach(waitResolve::add); clazz = waitResolve.poll(); } @@ -535,7 +535,7 @@ public class ClassUtils { } Method[] methods = Arrays.stream(tClass.getMethods()) .collect(Collectors.toList()) - .toArray(new Method[] {}); + .toArray(new Method[]{}); List mns = new ArrayList<>(); // method names. boolean hasMethod = hasMethods(methods); if (hasMethod) { @@ -551,6 +551,44 @@ public class ClassUtils { return mns.toArray(new String[0]); } + public static boolean isMatch(Class from, Class to) { + if (from == to) { + return true; + } + boolean isMatch; + if (from.isPrimitive()) { + isMatch = matchPrimitive(from, to); + } else if (to.isPrimitive()) { + isMatch = matchPrimitive(to, from); + } else { + isMatch = to.isAssignableFrom(from); + } + return isMatch; + } + + private static boolean matchPrimitive(Class from, Class to) { + if (from == boolean.class) { + return to == Boolean.class; + } else if (from == byte.class) { + return to == Byte.class; + } else if (from == char.class) { + return to == Character.class; + } else if (from == short.class) { + return to == Short.class; + } else if (from == int.class) { + return to == Integer.class; + } else if (from == long.class) { + return to == Long.class; + } else if (from == float.class) { + return to == Float.class; + } else if (from == double.class) { + return to == Double.class; + } else if (from == void.class) { + return to == Void.class; + } + return false; + } + /** * get method name array. * @@ -562,7 +600,7 @@ public class ClassUtils { } Method[] methods = Arrays.stream(tClass.getMethods()) .collect(Collectors.toList()) - .toArray(new Method[] {}); + .toArray(new Method[]{}); List dmns = new ArrayList<>(); // method names. boolean hasMethod = hasMethods(methods); if (hasMethod) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/JRE.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/JRE.java index 5bba8a70da..8bc095d61e 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/JRE.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/JRE.java @@ -117,7 +117,7 @@ public enum JRE { return OTHER; } } catch (Exception e) { - logger.debug("can't determine current JRE version, if JRE version is 8 but java.version is null", e); + logger.debug("Can't determine current JRE version (maybe java.version is null), assuming that JRE version is 8.", e); } // default java 8 return JAVA_8; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/JsonUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/JsonUtils.java index 5e3ff677ea..88fd380b5f 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/JsonUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/JsonUtils.java @@ -23,13 +23,16 @@ import org.apache.dubbo.common.json.impl.FastJsonImpl; import org.apache.dubbo.common.json.impl.GsonImpl; import org.apache.dubbo.common.json.impl.JacksonImpl; +import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; +import java.util.Map; public class JsonUtils { + private static volatile JSON json; - public static JSON getJson() { + protected static JSON getJson() { if (json == null) { synchronized (JsonUtils.class) { if (json == null) { @@ -86,4 +89,76 @@ public class JsonUtils { return json; } + /** + * @deprecated for uts only + */ + @Deprecated + protected static void setJson(JSON json) { + JsonUtils.json = json; + } + + public static T toJavaObject(String json, Type type) { + return getJson().toJavaObject(json, type); + } + + + public static List toJavaList(String json, Class clazz) { + return getJson().toJavaList(json, clazz); + } + + + public static String toJson(Object obj) { + return getJson().toJson(obj); + } + + + public static List getList(Map obj, String key) { + return getJson().getList(obj, key); + } + + + public static List> getListOfObjects(Map obj, String key) { + return getJson().getListOfObjects(obj, key); + } + + + public static List getListOfStrings(Map obj, String key) { + return getJson().getListOfStrings(obj, key); + } + + + public static Map getObject(Map obj, String key) { + return getJson().getObject(obj, key); + } + + + public static Double getNumberAsDouble(Map obj, String key) { + return getJson().getNumberAsDouble(obj, key); + } + + + public static Integer getNumberAsInteger(Map obj, String key) { + return getJson().getNumberAsInteger(obj, key); + } + + + public static Long getNumberAsLong(Map obj, String key) { + return getJson().getNumberAsLong(obj, key); + } + + + public static String getString(Map obj, String key) { + return getJson().getString(obj, key); + } + + + public static List> checkObjectList(List rawList) { + return getJson().checkObjectList(rawList); + } + + + public static List checkStringList(List rawList) { + return getJson().checkStringList(rawList); + } + } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java index 58f902f599..d33fcea5b0 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java @@ -23,7 +23,6 @@ import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.logger.support.FailsafeLogger; import org.apache.dubbo.rpc.model.ScopeModel; - import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; @@ -251,6 +250,8 @@ public final class NetUtils { private static volatile String HOST_ADDRESS; + private static volatile String HOST_NAME; + private static volatile String HOST_ADDRESS_V6; public static String getLocalHost() { @@ -585,11 +586,15 @@ public final class NetUtils { } public static String getLocalHostName() { - try { - return InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - return getLocalAddress().getHostName(); + if (HOST_NAME != null) { + return HOST_NAME; } + try { + HOST_NAME= InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + HOST_NAME= Optional.ofNullable(getLocalAddress()).map(k->k.getHostName()).orElse(null); + } + return HOST_NAME; } /** diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PathUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PathUtils.java index c291d792e6..b40e26dceb 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PathUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PathUtils.java @@ -40,8 +40,8 @@ public interface PathUtils { paths.addAll(asList(subPaths)); return normalize(paths.stream() - .filter(StringUtils::isNotEmpty) - .collect(Collectors.joining(SLASH))); + .filter(StringUtils::isNotEmpty) + .collect(Collectors.joining(SLASH))); } /** @@ -67,7 +67,6 @@ public interface PathUtils { while (normalizedPath.contains("//")) { normalizedPath = replace(normalizedPath, "//", "/"); } - return normalizedPath; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java index 200aa7b1be..60a78061f4 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java @@ -16,6 +16,12 @@ */ package org.apache.dubbo.common.utils; +import org.apache.dubbo.common.config.ConfigurationUtils; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.rpc.model.ApplicationModel; + import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -53,11 +59,6 @@ import java.util.concurrent.ConcurrentSkipListMap; import java.util.function.Consumer; import java.util.function.Supplier; -import org.apache.dubbo.common.config.ConfigurationUtils; -import org.apache.dubbo.common.constants.CommonConstants; -import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; -import org.apache.dubbo.common.logger.LoggerFactory; - import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_REFLECTIVE_OPERATION_FAILED; import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom; @@ -78,13 +79,15 @@ import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom; public class PojoUtils { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(PojoUtils.class); - private static final ConcurrentMap NAME_METHODS_CACHE = new ConcurrentHashMap(); - private static final ConcurrentMap, ConcurrentMap> CLASS_FIELD_CACHE = new ConcurrentHashMap, ConcurrentMap>(); + private static final ConcurrentMap NAME_METHODS_CACHE = new ConcurrentHashMap<>(); + private static final ConcurrentMap, ConcurrentMap> CLASS_FIELD_CACHE = new ConcurrentHashMap<>(); - private static final ConcurrentMap CLASS_NOT_FOUND_CACHE = new ConcurrentHashMap(); + private static final ConcurrentMap CLASS_NOT_FOUND_CACHE = new ConcurrentHashMap<>(); private static final Object NOT_FOUND_VALUE = new Object(); - private static final boolean GENERIC_WITH_CLZ = Boolean.parseBoolean(ConfigurationUtils.getProperty(CommonConstants.GENERIC_WITH_CLZ_KEY, "true")); + private static final boolean GENERIC_WITH_CLZ = Boolean.parseBoolean( + ConfigurationUtils.getProperty(ApplicationModel.defaultModel(), CommonConstants.GENERIC_WITH_CLZ_KEY, "true")); + private static final List> CLASS_CAN_BE_STRING = Arrays.asList(Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Boolean.class, Character.class); @@ -121,7 +124,7 @@ public class PojoUtils { } public static Object generalize(Object pojo) { - return generalize(pojo, new IdentityHashMap()); + return generalize(pojo, new IdentityHashMap<>()); } @SuppressWarnings("unchecked") @@ -173,7 +176,7 @@ public class PojoUtils { if (pojo instanceof Collection) { Collection src = (Collection) pojo; int len = src.size(); - Collection dest = (pojo instanceof List) ? new ArrayList(len) : new HashSet(len); + Collection dest = (pojo instanceof List) ? new ArrayList<>(len) : new HashSet<>(len); history.put(pojo, dest); for (Object obj : src) { dest.add(generalize(obj, history)); @@ -189,7 +192,7 @@ public class PojoUtils { } return dest; } - Map map = new HashMap(); + Map map = new HashMap<>(); history.put(pojo, map); if (GENERIC_WITH_CLZ) { map.put("class", pojo.getClass().getName()); @@ -228,16 +231,16 @@ public class PojoUtils { } public static Object realize(Object pojo, Class type) { - return realize0(pojo, type, null, new IdentityHashMap()); + return realize0(pojo, type, null, new IdentityHashMap<>()); } public static Object realize(Object pojo, Class type, Type genericType) { - return realize0(pojo, type, genericType, new IdentityHashMap()); + return realize0(pojo, type, genericType, new IdentityHashMap<>()); } private static class PojoInvocationHandler implements InvocationHandler { - private Map map; + private final Map map; public PojoInvocationHandler(Map map) { this.map = map; @@ -259,7 +262,7 @@ public class PojoUtils { value = map.get(methodName.substring(0, 1).toLowerCase() + methodName.substring(1)); } if (value instanceof Map && !Map.class.isAssignableFrom(method.getReturnType())) { - value = realize0((Map) value, method.getReturnType(), null, new IdentityHashMap()); + value = realize0(value, method.getReturnType(), null, new IdentityHashMap<>()); } return value; } @@ -268,10 +271,10 @@ public class PojoUtils { @SuppressWarnings("unchecked") private static Collection createCollection(Class type, int len) { if (type.isAssignableFrom(ArrayList.class)) { - return new ArrayList(len); + return new ArrayList<>(len); } if (type.isAssignableFrom(HashSet.class)) { - return new HashSet(len); + return new HashSet<>(len); } if (!type.isInterface() && !Modifier.isAbstract(type.getModifiers())) { try { @@ -280,7 +283,7 @@ public class PojoUtils { // ignore } } - return new ArrayList(); + return new ArrayList<>(); } private static Map createMap(Map src) { @@ -318,7 +321,7 @@ public class PojoUtils { } if (result == null) { - result = new HashMap(); + result = new HashMap<>(); } return result; @@ -326,6 +329,10 @@ public class PojoUtils { @SuppressWarnings({"unchecked", "rawtypes"}) private static Object realize0(Object pojo, Class type, Type genericType, final Map history) { + return realize1(pojo, type, genericType, new HashMap<>(8), history); + } + + private static Object realize1(Object pojo, Class type, Type genericType, final Map mapParent, final Map history) { if (pojo == null) { return null; } @@ -349,6 +356,19 @@ public class PojoUtils { history.put(pojo, pojo); + Map mapGeneric = new HashMap<>(8); + mapGeneric.putAll(mapParent); + TypeVariable>[] typeParameters = type.getTypeParameters(); + if(genericType instanceof ParameterizedType && typeParameters.length > 0) { + ParameterizedType parameterizedType = (ParameterizedType)genericType; + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + for (int i = 0; i < typeParameters.length; i++) { + if(!(actualTypeArguments[i] instanceof TypeVariable)) { + mapGeneric.put(typeParameters[i].getTypeName(), actualTypeArguments[i]); + } + } + } + if (pojo.getClass().isArray()) { if (Collection.class.isAssignableFrom(type)) { Class ctype = pojo.getClass().getComponentType(); @@ -357,7 +377,7 @@ public class PojoUtils { history.put(pojo, dest); for (int i = 0; i < len; i++) { Object obj = Array.get(pojo, i); - Object value = realize0(obj, ctype, null, history); + Object value = realize1(obj, ctype, null, mapGeneric, history); dest.add(value); } return dest; @@ -368,7 +388,7 @@ public class PojoUtils { history.put(pojo, dest); for (int i = 0; i < len; i++) { Object obj = Array.get(pojo, i); - Object value = realize0(obj, ctype, null, history); + Object value = realize1(obj, ctype, null, mapGeneric, history); Array.set(dest, i, value); } return dest; @@ -384,7 +404,7 @@ public class PojoUtils { history.put(pojo, dest); int i = 0; for (Object obj : src) { - Object value = realize0(obj, ctype, null, history); + Object value = realize1(obj, ctype, null, mapGeneric, history); Array.set(dest, i, value); i++; } @@ -400,7 +420,7 @@ public class PojoUtils { if (keyType instanceof Class) { keyClazz = (Class) keyType; } - Object value = realize0(obj, keyClazz, keyType, history); + Object value = realize1(obj, keyClazz, keyType, mapGeneric, history); dest.add(value); } return dest; @@ -483,8 +503,8 @@ public class PojoUtils { valueClazz = entry.getValue() == null ? null : entry.getValue().getClass(); } - Object key = keyClazz == null ? entry.getKey() : realize0(entry.getKey(), keyClazz, keyType, history); - Object value = valueClazz == null ? entry.getValue() : realize0(entry.getValue(), valueClazz, valueType, history); + Object key = keyClazz == null ? entry.getKey() : realize1(entry.getKey(), keyClazz, keyType, mapGeneric, history); + Object value = valueClazz == null ? entry.getValue() : realize1(entry.getValue(), valueClazz, valueType, mapGeneric, history); result.put(key, value); } return result; @@ -506,6 +526,7 @@ public class PojoUtils { } history.put(pojo, dest); + for (Map.Entry entry : map.entrySet()) { Object key = entry.getKey(); if (key instanceof String) { @@ -518,8 +539,24 @@ public class PojoUtils { if (!method.isAccessible()) { method.setAccessible(true); } - Type ptype = method.getGenericParameterTypes()[0]; - value = realize0(value, method.getParameterTypes()[0], ptype, history); + Type containType = mapGeneric.get(field.getGenericType().getTypeName()); + if(containType != null) { + //is generic + if(containType instanceof ParameterizedType) { + value = realize1(value, (Class) ((ParameterizedType)containType).getRawType(), containType, mapGeneric, history); + } + else if (containType instanceof Class){ + value = realize1(value, (Class) containType, containType, mapGeneric, history); + } + else { + Type ptype = method.getGenericParameterTypes()[0]; + value = realize1(value, method.getParameterTypes()[0], ptype, mapGeneric, history); + } + } + else { + Type ptype = method.getGenericParameterTypes()[0]; + value = realize1(value, method.getParameterTypes()[0], ptype, mapGeneric, history); + } try { method.invoke(dest, value); } catch (Exception e) { @@ -529,7 +566,7 @@ public class PojoUtils { throw new RuntimeException(exceptionDescription, e); } } else if (field != null) { - value = realize0(value, field.getType(), field.getGenericType(), history); + value = realize1(value, field.getType(), field.getGenericType(), mapGeneric, history); try { field.set(dest, value); } catch (IllegalAccessException e) { @@ -672,10 +709,16 @@ public class PojoUtils { if (CLASS_FIELD_CACHE.containsKey(cls) && CLASS_FIELD_CACHE.get(cls).containsKey(fieldName)) { return CLASS_FIELD_CACHE.get(cls).get(fieldName); } - try { - result = cls.getDeclaredField(fieldName); - result.setAccessible(true); - } catch (NoSuchFieldException e) { + for(Class acls = cls; acls != null; acls = acls.getSuperclass()) { + try { + result = acls.getDeclaredField(fieldName); + if (!Modifier.isPublic(result.getModifiers())) { + result.setAccessible(true); + } + } catch (NoSuchFieldException e) { + } + } + if(result == null) { for (Field field : cls.getFields()) { if (fieldName.equals(field.getName()) && ReflectUtils.isPublicInstanceField(field)) { result = field; @@ -683,6 +726,7 @@ public class PojoUtils { } } } + if (result != null) { ConcurrentMap fields = CLASS_FIELD_CACHE.computeIfAbsent(cls, k -> new ConcurrentHashMap<>()); fields.putIfAbsent(fieldName, result); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java index 8f7339bf8d..b32cf2d1e4 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java @@ -872,7 +872,7 @@ public final class ReflectUtils { return EMPTY_CLASS_ARRAY; } - List> cs = new ArrayList>(); + List> cs = new ArrayList<>(); Matcher m = DESC_PATTERN.matcher(desc); while (m.find()) { cs.add(desc2class(cl, m.group())); @@ -900,7 +900,7 @@ public final class ReflectUtils { } Method method; if (parameterTypes == null) { - List finded = new ArrayList(); + List finded = new ArrayList<>(); for (Method m : clazz.getMethods()) { if (m.getName().equals(methodName)) { finded.add(m); @@ -1140,7 +1140,7 @@ public final class ReflectUtils { } public static Map getBeanPropertyFields(Class cl) { - Map properties = new HashMap(); + Map properties = new HashMap<>(); for (; cl != null; cl = cl.getSuperclass()) { Field[] fields = cl.getDeclaredFields(); for (Field field : fields) { @@ -1159,7 +1159,7 @@ public final class ReflectUtils { } public static Map getBeanPropertyReadMethods(Class cl) { - Map properties = new HashMap(); + Map properties = new HashMap<>(); for (; cl != null; cl = cl.getSuperclass()) { Method[] methods = cl.getDeclaredMethods(); for (Method method : methods) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityConfigurator.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityConfigurator.java index a02584f0bb..526db90138 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityConfigurator.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityConfigurator.java @@ -56,7 +56,7 @@ public class SerializeSecurityConfigurator implements ScopeClassLoaderListener interfaceClass, boolean ignoreInvalidMethodConfig) { + protected boolean verifyMethodConfig(MethodConfig methodConfig, Class interfaceClass, boolean ignoreInvalidMethodConfig) { String methodName = methodConfig.getName(); if (StringUtils.isEmpty(methodName)) { String msg = " name attribute is required! Please check: " + @@ -396,7 +396,12 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig { logger.warn(CONFIG_NO_METHOD_FOUND, "", "", msg); return false; } else { - throw new IllegalStateException(msg); + if (!isNeedCheckMethod()) { + msg = "Generic call: " + msg; + logger.warn(CONFIG_NO_METHOD_FOUND, "", "", msg); + } else { + throw new IllegalStateException(msg); + } } } return true; @@ -413,6 +418,11 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig { return null; } + @Transient + protected boolean isNeedCheckMethod() { + return true; + } + private boolean hasArgumentConfigProps(Map configProperties, String methodName, int argIndex) { String argPrefix = methodName + "." + argIndex + "."; return ConfigurationUtils.hasSubProperties(configProperties, argPrefix); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java index 6ac7a43948..e659b10b6f 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java @@ -183,6 +183,11 @@ public abstract class AbstractReferenceConfig extends AbstractInterfaceConfig { } } + @Override + protected boolean isNeedCheckMethod() { + return StringUtils.isEmpty(getGeneric()); + } + /** * @return * @deprecated instead, use the parameter scope to judge if it's in jvm, scope=local diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java index 78198f2462..63dcfb5375 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java @@ -16,14 +16,6 @@ */ package org.apache.dubbo.config; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - import org.apache.dubbo.common.compiler.support.AdaptiveCompiler; import org.apache.dubbo.common.infra.InfraAdapter; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; @@ -33,6 +25,14 @@ import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_VERSION_KEY; @@ -40,7 +40,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; import static org.apache.dubbo.common.constants.CommonConstants.DUMP_DIRECTORY; import static org.apache.dubbo.common.constants.CommonConstants.DUMP_ENABLE; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE; -import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_ISOLATION; import static org.apache.dubbo.common.constants.CommonConstants.HOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.LIVENESS_PROBE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METADATA_KEY; @@ -274,7 +274,7 @@ public class ApplicationConfig extends AbstractConfig { } } if (executorManagementMode == null) { - executorManagementMode = EXECUTOR_MANAGEMENT_MODE_DEFAULT; + executorManagementMode = EXECUTOR_MANAGEMENT_MODE_ISOLATION; } if (enableFileCache == null) { enableFileCache = Boolean.TRUE; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java index 07a1e5f425..f01abbb896 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.config.nested.PrometheusConfig; +import org.apache.dubbo.config.nested.HistogramConfig; import org.apache.dubbo.config.support.Nested; import org.apache.dubbo.rpc.model.ApplicationModel; @@ -38,17 +39,22 @@ public class MetricsConfig extends AbstractConfig { /** * Enable jvm metrics when collecting. */ - private Boolean enableJvmMetrics; + private Boolean enableJvm; + + /** + * Enable threadpool metrics when collecting. + */ + private Boolean enableThreadpool; /** * Enable registry metrics. */ - private Boolean enableRegistryMetrics; + private Boolean enableRegistry; /** * Enable metadata metrics. */ - private Boolean enableMetadataMetrics; + private Boolean enableMetadata; /** * @deprecated After metrics config is refactored. @@ -69,6 +75,9 @@ public class MetricsConfig extends AbstractConfig { @Nested private AggregationConfig aggregation; + @Nested + private HistogramConfig histogram; + private String exportServiceProtocol; private Integer exportServicePort; @@ -100,20 +109,20 @@ public class MetricsConfig extends AbstractConfig { this.protocol = protocol; } - public Boolean getEnableJvmMetrics() { - return enableJvmMetrics; + public Boolean getEnableJvm() { + return enableJvm; } - public void setEnableJvmMetrics(Boolean enableJvmMetrics) { - this.enableJvmMetrics = enableJvmMetrics; + public void setEnableJvm(Boolean enableJvm) { + this.enableJvm = enableJvm; } - public Boolean getEnableRegistryMetrics() { - return enableRegistryMetrics; + public Boolean getEnableRegistry() { + return enableRegistry; } - public void setEnableRegistryMetrics(Boolean enableRegistryMetrics) { - this.enableRegistryMetrics = enableRegistryMetrics; + public void setEnableRegistry(Boolean enableRegistry) { + this.enableRegistry = enableRegistry; } public String getPort() { @@ -140,6 +149,14 @@ public class MetricsConfig extends AbstractConfig { this.aggregation = aggregation; } + public HistogramConfig getHistogram() { + return histogram; + } + + public void setHistogram(HistogramConfig histogram) { + this.histogram = histogram; + } + public String getExportServiceProtocol() { return exportServiceProtocol; } @@ -156,12 +173,19 @@ public class MetricsConfig extends AbstractConfig { this.exportServicePort = exportServicePort; } - public Boolean getEnableMetadataMetrics() { - return enableMetadataMetrics; + public Boolean getEnableMetadata() { + return enableMetadata; } - public void setEnableMetadataMetrics(Boolean enableMetadataMetrics) { - this.enableMetadataMetrics = enableMetadataMetrics; + public void setEnableMetadata(Boolean enableMetadata) { + this.enableMetadata = enableMetadata; + } + + public Boolean getEnableThreadpool() { + return enableThreadpool; + } + + public void setEnableThreadpool(Boolean enableThreadpool) { + this.enableThreadpool = enableThreadpool; } } - diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/TracingConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/TracingConfig.java new file mode 100644 index 0000000000..62b134f35c --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/TracingConfig.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.config; + +import org.apache.dubbo.config.nested.BaggageConfig; +import org.apache.dubbo.config.nested.PropagationConfig; +import org.apache.dubbo.config.nested.SamplingConfig; +import org.apache.dubbo.config.support.Nested; +import org.apache.dubbo.rpc.model.ApplicationModel; + +/** + * TracingConfig + */ +public class TracingConfig extends AbstractConfig { + + private static final long serialVersionUID = -9089919311611546383L; + + private Boolean enabled = false; + + /** + * Sampling configuration. + */ + @Nested + private SamplingConfig sampling = new SamplingConfig(); + + /** + * Baggage configuration. + */ + @Nested + private BaggageConfig baggage = new BaggageConfig(); + + /** + * Propagation configuration. + */ + @Nested + private PropagationConfig propagation = new PropagationConfig(); + + public TracingConfig() { + } + + public TracingConfig(ApplicationModel applicationModel) { + super(applicationModel); + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public SamplingConfig getSampling() { + return sampling; + } + + public void setSampling(SamplingConfig sampling) { + this.sampling = sampling; + } + + public BaggageConfig getBaggage() { + return baggage; + } + + public void setBaggage(BaggageConfig baggage) { + this.baggage = baggage; + } + + public PropagationConfig getPropagation() { + return propagation; + } + + public void setPropagation(PropagationConfig propagation) { + this.propagation = propagation; + } +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/context/AbstractConfigManager.java b/dubbo-common/src/main/java/org/apache/dubbo/config/context/AbstractConfigManager.java index ed587a47e5..8a5dfdd79b 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/context/AbstractConfigManager.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/context/AbstractConfigManager.java @@ -39,6 +39,7 @@ import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfigBase; import org.apache.dubbo.config.SslConfig; +import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; @@ -92,6 +93,7 @@ public abstract class AbstractConfigManager extends LifecycleAdapter { uniqueConfigTypes.add(ApplicationConfig.class); uniqueConfigTypes.add(MonitorConfig.class); uniqueConfigTypes.add(MetricsConfig.class); + uniqueConfigTypes.add(TracingConfig.class); uniqueConfigTypes.add(SslConfig.class); // unique config in each module @@ -127,7 +129,7 @@ public abstract class AbstractConfigManager extends LifecycleAdapter { // dubbo.config.ignore-duplicated-interface String ignoreDuplicatedInterfaceStr = (String) configuration - .getProperty(ConfigKeys.DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE); + .getProperty(ConfigKeys.DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE); if (ignoreDuplicatedInterfaceStr != null) { this.ignoreDuplicatedInterface = Boolean.parseBoolean(ignoreDuplicatedInterfaceStr); } @@ -192,7 +194,7 @@ public abstract class AbstractConfigManager extends LifecycleAdapter { * @throws IllegalStateException */ private C addIfAbsent(C config, Map configsMap) - throws IllegalStateException { + throws IllegalStateException { if (config == null || configsMap == null) { return config; @@ -216,8 +218,8 @@ public abstract class AbstractConfigManager extends LifecycleAdapter { if (existedConfig != null && !isEquals(existedConfig, config)) { String type = config.getClass().getSimpleName(); logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", String.format("Duplicate %s found, there already has one default %s or more than two %ss have the same id, " + - "you can try to give each %s a different id, override previous config with later config. id: %s, prev: %s, later: %s", - type, type, type, type, key, existedConfig, config)); + "you can try to give each %s a different id, override previous config with later config. id: %s, prev: %s, later: %s", + type, type, type, type, key, existedConfig, config)); } // override existed config if any @@ -252,7 +254,7 @@ public abstract class AbstractConfigManager extends LifecycleAdapter { return null; } else if (size > 1) { throw new IllegalStateException("Expected single instance of " + configType + ", but found " + size + - " instances, please remove redundant configs. instances: " + configsMap.values()); + " instances, please remove redundant configs. instances: " + configsMap.values()); } return (C) configsMap.values().iterator().next(); } @@ -335,11 +337,11 @@ public abstract class AbstractConfigManager extends LifecycleAdapter { // try to find config by name if (ReflectUtils.hasMethod(cls, CONFIG_NAME_READ_METHOD)) { List list = configsMap.values().stream() - .filter(cfg -> name.equals(getConfigName(cfg))) - .collect(Collectors.toList()); + .filter(cfg -> name.equals(getConfigName(cfg))) + .collect(Collectors.toList()); if (list.size() > 1) { throw new IllegalStateException("Found more than one config by name: " + name + - ", instances: " + list + ". Please remove redundant configs or get config by id."); + ", instances: " + list + ". Please remove redundant configs or get config by id."); } else if (list.size() == 1) { return list.get(0); } @@ -364,8 +366,8 @@ public abstract class AbstractConfigManager extends LifecycleAdapter { // 2. find equal config prevConfig = values.stream() - .filter(val -> isEquals(val, config)) - .findFirst(); + .filter(val -> isEquals(val, config)) + .findFirst(); return prevConfig; } @@ -408,18 +410,18 @@ public abstract class AbstractConfigManager extends LifecycleAdapter { static List getDefaultConfigs(Map configsMap) { // find isDefault() == true List list = configsMap.values() - .stream() - .filter(c -> TRUE.equals(AbstractConfigManager.isDefaultConfig(c))) - .collect(Collectors.toList()); + .stream() + .filter(c -> TRUE.equals(AbstractConfigManager.isDefaultConfig(c))) + .collect(Collectors.toList()); if (list.size() > 0) { return list; } // find isDefault() == null list = configsMap.values() - .stream() - .filter(c -> AbstractConfigManager.isDefaultConfig(c) == null) - .collect(Collectors.toList()); + .stream() + .filter(c -> AbstractConfigManager.isDefaultConfig(c) == null) + .collect(Collectors.toList()); return list; // exclude isDefault() == false @@ -430,7 +432,7 @@ public abstract class AbstractConfigManager extends LifecycleAdapter { C oldOne = configsMap.values().iterator().next(); String configName = oldOne.getClass().getSimpleName(); String msgPrefix = "Duplicate Configs found for " + configName + ", only one unique " + configName + - " is allowed for one application. previous: " + oldOne + ", later: " + config + ". According to config mode [" + configMode + "], "; + " is allowed for one application. previous: " + oldOne + ", later: " + config + ". According to config mode [" + configMode + "], "; switch (configMode) { case STRICT: { if (!isEquals(oldOne, config)) { @@ -625,9 +627,10 @@ public abstract class AbstractConfigManager extends LifecycleAdapter { */ protected boolean isRequired(Class clazz) { if (clazz == RegistryConfig.class || - clazz == MetadataReportConfig.class || - clazz == MonitorConfig.class || - clazz == MetricsConfig.class) { + clazz == MetadataReportConfig.class || + clazz == MonitorConfig.class || + clazz == MetricsConfig.class || + clazz == TracingConfig.class) { return false; } return true; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java index dba0870771..ec7629bbf6 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java @@ -32,6 +32,7 @@ import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.SslConfig; +import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Arrays; @@ -59,8 +60,8 @@ public class ConfigManager extends AbstractConfigManager implements ApplicationE public ConfigManager(ApplicationModel applicationModel) { super(applicationModel, Arrays.asList(ApplicationConfig.class, MonitorConfig.class, - MetricsConfig.class, SslConfig.class, ProtocolConfig.class, RegistryConfig.class, ConfigCenterConfig.class, - MetadataReportConfig.class)); + MetricsConfig.class, SslConfig.class, ProtocolConfig.class, RegistryConfig.class, ConfigCenterConfig.class, + MetadataReportConfig.class, TracingConfig.class)); } @@ -105,6 +106,15 @@ public class ConfigManager extends AbstractConfigManager implements ApplicationE return ofNullable(getSingleConfig(getTagName(MetricsConfig.class))); } + @DisableInject + public void setTracing(TracingConfig tracing) { + addConfig(tracing); + } + + public Optional getTracing() { + return ofNullable(getSingleConfig(getTagName(TracingConfig.class))); + } + @DisableInject public void setSsl(SslConfig sslConfig) { addConfig(sslConfig); @@ -223,6 +233,7 @@ public class ConfigManager extends AbstractConfigManager implements ApplicationE getApplication().ifPresent(ApplicationConfig::refresh); getMonitor().ifPresent(MonitorConfig::refresh); getMetrics().ifPresent(MetricsConfig::refresh); + getTracing().ifPresent(TracingConfig::refresh); getSsl().ifPresent(SslConfig::refresh); getProtocols().forEach(ProtocolConfig::refresh); @@ -243,6 +254,8 @@ public class ConfigManager extends AbstractConfigManager implements ApplicationE // load dubbo.metrics.xxx loadConfigsOfTypeFromProps(MetricsConfig.class); + loadConfigsOfTypeFromProps(TracingConfig.class); + // load multiple config types: // load dubbo.protocols.xxx loadConfigsOfTypeFromProps(ProtocolConfig.class); @@ -269,12 +282,13 @@ public class ConfigManager extends AbstractConfigManager implements ApplicationE private void checkConfigs() { // check config types (ignore metadata-center) List> multipleConfigTypes = Arrays.asList( - ApplicationConfig.class, - ProtocolConfig.class, - RegistryConfig.class, - MonitorConfig.class, - MetricsConfig.class, - SslConfig.class); + ApplicationConfig.class, + ProtocolConfig.class, + RegistryConfig.class, + MonitorConfig.class, + MetricsConfig.class, + TracingConfig.class, + SslConfig.class); for (Class configType : multipleConfigTypes) { checkDefaultAndValidateConfigs(configType); @@ -290,7 +304,7 @@ public class ConfigManager extends AbstractConfigManager implements ApplicationE ProtocolConfig prevProtocol = protocolPortMap.get(port); if (prevProtocol != null) { throw new IllegalStateException("Duplicated port used by protocol configs, port: " + port + - ", configs: " + Arrays.asList(prevProtocol, protocol)); + ", configs: " + Arrays.asList(prevProtocol, protocol)); } protocolPortMap.put(port, protocol); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ModuleConfigManager.java b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ModuleConfigManager.java index 72d3e97efc..0527fdaf18 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ModuleConfigManager.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ModuleConfigManager.java @@ -36,6 +36,7 @@ import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfigBase; import org.apache.dubbo.config.SslConfig; +import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Arrays; @@ -352,6 +353,10 @@ public class ModuleConfigManager extends AbstractConfigManager implements Module return applicationConfigManager.getMetrics(); } + public Optional getTracing() { + return applicationConfigManager.getTracing(); + } + public Optional getSsl() { return applicationConfigManager.getSsl(); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/BaggageConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/BaggageConfig.java new file mode 100644 index 0000000000..beba9b5ddd --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/BaggageConfig.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.config.nested; + +import org.apache.dubbo.config.support.Nested; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class BaggageConfig implements Serializable { + + private Boolean enabled = true; + + /** + * Correlation configuration. + */ + @Nested + private Correlation correlation = new Correlation(); + + /** + * List of fields that are referenced the same in-process as it is on the wire. + * For example, the field "x-vcap-request-id" would be set as-is including the + * prefix. + */ + private List remoteFields = new ArrayList<>(); + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public Correlation getCorrelation() { + return correlation; + } + + public void setCorrelation(Correlation correlation) { + this.correlation = correlation; + } + + public List getRemoteFields() { + return remoteFields; + } + + public void setRemoteFields(List remoteFields) { + this.remoteFields = remoteFields; + } + + public static class Correlation implements Serializable { + + /** + * Whether to enable correlation of the baggage context with logging contexts. + */ + private boolean enabled = true; + + /** + * List of fields that should be correlated with the logging context. That + * means that these fields would end up as key-value pairs in e.g. MDC. + */ + private List fields = new ArrayList<>(); + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public List getFields() { + return this.fields; + } + + public void setFields(List fields) { + this.fields = fields; + } + + } +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/HistogramConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/HistogramConfig.java new file mode 100644 index 0000000000..53ef9ac9e4 --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/HistogramConfig.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.config.nested; + +import java.io.Serializable; + +public class HistogramConfig implements Serializable { + + private Boolean enabled; + + private Integer[] bucketsMs; + + private Integer minExpectedMs; + + private Integer maxExpectedMs; + + private Boolean enabledPercentiles; + + private double[] percentiles; + + private Integer distributionStatisticExpiryMin; + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public Integer[] getBucketsMs() { + return bucketsMs; + } + + public void setBucketsMs(Integer[] bucketsMs) { + this.bucketsMs = bucketsMs; + } + + public Integer getMinExpectedMs() { + return minExpectedMs; + } + + public void setMinExpectedMs(Integer minExpectedMs) { + this.minExpectedMs = minExpectedMs; + } + + public Integer getMaxExpectedMs() { + return maxExpectedMs; + } + + public void setMaxExpectedMs(Integer maxExpectedMs) { + this.maxExpectedMs = maxExpectedMs; + } + + public Boolean getEnabledPercentiles() { + return enabledPercentiles; + } + + public void setEnabledPercentiles(Boolean enabledPercentiles) { + this.enabledPercentiles = enabledPercentiles; + } + + public double[] getPercentiles() { + return percentiles; + } + + public void setPercentiles(double[] percentiles) { + this.percentiles = percentiles; + } + + public Integer getDistributionStatisticExpiryMin() { + return distributionStatisticExpiryMin; + } + + public void setDistributionStatisticExpiryMin(Integer distributionStatisticExpiryMin) { + this.distributionStatisticExpiryMin = distributionStatisticExpiryMin; + } +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/RequestEvent.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java similarity index 70% rename from dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/RequestEvent.java rename to dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java index cfa639cae2..8e52353323 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/RequestEvent.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java @@ -14,27 +14,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.apache.dubbo.config.nested; -package org.apache.dubbo.metrics.event; +import java.io.Serializable; -/** - * RequestEvent. - */ -public class RequestEvent extends MetricsEvent { - private Type type; +public class PropagationConfig implements Serializable { - public RequestEvent(Object source, Type type) { - super(source); - this.type = type; - } + public static final String B3 = "B3"; - public Type getType() { + public static final String W3C = "W3C"; + + /** + * Tracing context propagation type. + */ + private String type = W3C; + + public String getType() { return type; } - public void setType(Type type) { + public void setType(String type) { this.type = type; } - } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/SamplingConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/SamplingConfig.java new file mode 100644 index 0000000000..a605527190 --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/SamplingConfig.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.config.nested; + +import java.io.Serializable; + +public class SamplingConfig implements Serializable { + + /** + * Probability in the range from 0.0 to 1.0 that a trace will be sampled. + */ + private float probability = 0.10f; + + public float getProbability() { + return this.probability; + } + + public void setProbability(float probability) { + this.probability = probability; + } +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuilder.java b/dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuilder.java index 1bfafb5c80..1367b5d551 100755 --- a/dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuilder.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuilder.java @@ -114,7 +114,7 @@ public final class ServiceDefinitionBuilder { */ public static String schema(final Class clazz) { ServiceDefinition sd = build(clazz); - return JsonUtils.getJson().toJson(sd); + return JsonUtils.toJson(sd); } private ServiceDefinitionBuilder() { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/AbstractIsolationExecutorSupport.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/AbstractIsolationExecutorSupport.java index a8a71572d0..2a228d3cc5 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/AbstractIsolationExecutorSupport.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/AbstractIsolationExecutorSupport.java @@ -50,15 +50,17 @@ public abstract class AbstractIsolationExecutorSupport implements ExecutorSuppor for (URL serviceUrl : serviceUrls) { if (serviceUrl.getProtocol().equals(url.getProtocol()) && serviceUrl.getPort() == url.getPort()) { - return executorRepository.getExecutor(serviceUrl); + return executorRepository.getExecutor(providerModel, serviceUrl); } } - return executorRepository.getExecutor(serviceUrls.get(0)); + return executorRepository.getExecutor(providerModel, serviceUrls.get(0)); } - protected abstract ServiceKey getServiceKey(Object data); + protected ServiceKey getServiceKey(Object data) { + return null; + } - private ProviderModel getProviderModel(Object data) { + protected ProviderModel getProviderModel(Object data) { ServiceKey serviceKey = getServiceKey(data); if (serviceKey == null) { return null; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/DefaultIsolationExecutorSupportFactory.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/DefaultIsolationExecutorSupportFactory.java new file mode 100644 index 0000000000..0cdfd26f16 --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/DefaultIsolationExecutorSupportFactory.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.executor; + +import org.apache.dubbo.common.URL; + +public class DefaultIsolationExecutorSupportFactory implements IsolationExecutorSupportFactory { + @Override + public ExecutorSupport createIsolationExecutorSupport(URL url) { + return new DefaultExecutorSupport(url); + } +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/IsolationExecutorSupportFactory.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/IsolationExecutorSupportFactory.java index b610e9ac93..b3f4a551d8 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/IsolationExecutorSupportFactory.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/IsolationExecutorSupportFactory.java @@ -17,23 +17,19 @@ package org.apache.dubbo.rpc.executor; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.model.ApplicationModel; -import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; - -@SPI +@SPI("default") public interface IsolationExecutorSupportFactory { - @Adaptive(PROTOCOL_KEY) ExecutorSupport createIsolationExecutorSupport(URL url); static ExecutorSupport getIsolationExecutorSupport(URL url) { ApplicationModel applicationModel = url.getOrDefaultApplicationModel(); ExtensionLoader extensionLoader = applicationModel.getExtensionLoader(IsolationExecutorSupportFactory.class); - IsolationExecutorSupportFactory factory = extensionLoader.getAdaptiveExtension(); + IsolationExecutorSupportFactory factory = extensionLoader.getOrDefaultExtension(url.getProtocol()); return factory.createIsolationExecutorSupport(url); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java index c0f1e3cf10..0978143257 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java @@ -43,7 +43,7 @@ import java.util.concurrent.locks.Lock; * returned from them are of process scope. If you want to support multiple dubbo servers in one * single process, you may need to refactor those three classes. *

- * Represent a application which is using Dubbo and store basic metadata info for using + * Represent an application which is using Dubbo and store basic metadata info for using * during the processing of RPC invoking. *

* ApplicationModel includes many ProviderModel which is about published services diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/FrameworkServiceRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/FrameworkServiceRepository.java index 5d8df222e8..7aff29de0c 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/FrameworkServiceRepository.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/FrameworkServiceRepository.java @@ -16,7 +16,6 @@ */ package org.apache.dubbo.rpc.model; -import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.StringUtils; @@ -45,9 +44,6 @@ public class FrameworkServiceRepository { // useful to find a provider model quickly with serviceInterfaceName:version private final ConcurrentMap> providersWithoutGroup = new ConcurrentHashMap<>(); - // useful to find a url quickly with serviceInterfaceName:version - private final ConcurrentMap> providerUrlsWithoutGroup = new ConcurrentHashMap<>(); - public FrameworkServiceRepository(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @@ -67,7 +63,6 @@ public class FrameworkServiceRepository { providers.remove(providerModel.getServiceKey()); String keyWithoutGroup = keyWithoutGroup(providerModel.getServiceKey()); providersWithoutGroup.remove(keyWithoutGroup); - providerUrlsWithoutGroup.remove(keyWithoutGroup); } public ProviderModel lookupExportedServiceWithoutGroup(String key) { @@ -83,18 +78,10 @@ public class FrameworkServiceRepository { return providersWithoutGroup.get(key); } - public void registerProviderUrl(URL url) { - ConcurrentHashMapUtils.computeIfAbsent(providerUrlsWithoutGroup, keyWithoutGroup(url.getServiceKey()), (k) -> new CopyOnWriteArrayList<>()).add(url); - } - public ProviderModel lookupExportedService(String serviceKey) { return providers.get(serviceKey); } - public List lookupRegisteredProviderUrlsWithoutGroup(String key) { - return providerUrlsWithoutGroup.get(key); - } - public List allProviderModels() { return Collections.unmodifiableList(new ArrayList<>(providers.values())); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/PackableMethod.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/PackableMethod.java index 86bd18ad07..f21cc4d690 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/PackableMethod.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/PackableMethod.java @@ -35,6 +35,17 @@ public interface PackableMethod { byte[] pack(Object obj) throws IOException; } + interface WrapperUnPack extends UnPack { + + default Object unpack(byte[] data) throws IOException, ClassNotFoundException { + return unpack(data, false); + } + + Object unpack(byte[] data, boolean isReturnTriException) throws IOException, ClassNotFoundException; + + + } + interface UnPack { /** @@ -44,6 +55,7 @@ public interface PackableMethod { * @throws ClassNotFoundException when no class found */ Object unpack(byte[] data) throws IOException, ClassNotFoundException; + } default Object parseRequest(byte[] data) throws IOException, ClassNotFoundException { @@ -51,7 +63,15 @@ public interface PackableMethod { } default Object parseResponse(byte[] data) throws IOException, ClassNotFoundException { - return getResponseUnpack().unpack(data); + return parseResponse(data, false); + } + + default Object parseResponse(byte[] data, boolean isReturnTriException) throws IOException, ClassNotFoundException { + UnPack unPack = getResponseUnpack(); + if (unPack instanceof WrapperUnPack) { + return ((WrapperUnPack) unPack).unpack(data, isReturnTriException); + } + return unPack.unpack(data); } default byte[] packRequest(Object request) throws IOException { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/service/GenericException.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/service/GenericException.java index 1855b28ed5..23f8add206 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/service/GenericException.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/service/GenericException.java @@ -116,18 +116,18 @@ public class GenericException extends RuntimeException { if(this.useCause) { return getCause().getMessage(); } - return JsonUtils.getJson().toJson(GenericExceptionInfo.createNoStackTrace(genericExceptionInfo)); + return JsonUtils.toJson(GenericExceptionInfo.createNoStackTrace(genericExceptionInfo)); } public String getGenericException() { if(this.useCause) { return ((GenericException)getCause()).getGenericException(); } - return JsonUtils.getJson().toJson(genericExceptionInfo); + return JsonUtils.toJson(genericExceptionInfo); } public void setGenericException(String json) { - GenericExceptionInfo info = JsonUtils.getJson().toJavaObject(json, GenericExceptionInfo.class); + GenericExceptionInfo info = JsonUtils.toJavaObject(json, GenericExceptionInfo.class); if(info == null) { return; } diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory b/dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory new file mode 100644 index 0000000000..8cf24fbe2d --- /dev/null +++ b/dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory @@ -0,0 +1 @@ +default=org.apache.dubbo.rpc.executor.DefaultIsolationExecutorSupportFactory diff --git a/dubbo-common/src/main/resources/security/serialize.allowlist b/dubbo-common/src/main/resources/security/serialize.allowlist index ebaf7fc9fb..32c3ed4957 100644 --- a/dubbo-common/src/main/resources/security/serialize.allowlist +++ b/dubbo-common/src/main/resources/security/serialize.allowlist @@ -125,6 +125,8 @@ java.util.TreeMap java.util.TreeSet java.util.UUID java.util.WeakHashMap -org.apache.dubbo -com.alibaba.dubbo +org.apache.dubbo.metadata.MetadataInfo com.alibaba.com.caucho.hessian.io +com.alibaba.dubbo.rpc.service.GenericException +org.apache.dubbo.rpc.service.GenericException +org.apache.dubbo.rpc.RpcException diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLBuilderTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLBuilderTest.java index 9967a7bd02..219f9d9759 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLBuilderTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLBuilderTest.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.common; +import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.NetUtils; import org.junit.jupiter.api.Test; @@ -47,6 +48,24 @@ class URLBuilderTest { assertThat(url2.getVersion(), equalTo("1")); } + @Test + void testDefault() { + ServiceConfigURL url1 = URLBuilder.from(URL.valueOf("")) + .addParameter("timeout", "1234") + .addParameter("default.timeout", "5678") + .build(); + + assertThat(url1.getParameter("timeout"), equalTo("1234")); + assertThat(url1.getParameter("default.timeout"), equalTo("5678")); + + ServiceConfigURL url2 = URLBuilder.from(URL.valueOf("")) + .addParameter("default.timeout", "5678") + .build(); + + assertThat(url2.getParameter("timeout"), equalTo("5678")); + assertThat(url2.getParameter("default.timeout"), equalTo("5678")); + } + @Test void shouldSet() { URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); @@ -145,4 +164,4 @@ class URLBuilderTest { assertThat(url2.getParameter("version"), equalTo("1.0.0")); assertThat(url2.getParameter("absentKey"), equalTo("absentValue")); } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java index 6fccf104b0..67d13caa2b 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java @@ -45,6 +45,8 @@ class URLStrParserTest { testCases.add("dubbo://fe80:0:0:0:894:aeec:f37d:23e1%en0/path?abc=abc"); testCases.add("dubbo://[fe80:0:0:0:894:aeec:f37d:23e1]:20880/path?abc=abc"); testCases.add("nacos://192.168.1.1:8848?username=&password="); + testCases.add("dubbo://127.0.0.1?timeout=1234&default.timeout=5678"); + testCases.add("dubbo://127.0.0.1?default.timeout=5678"); errorDecodedCases.add("dubbo:192.168.1.1"); errorDecodedCases.add("://192.168.1.1"); @@ -81,4 +83,15 @@ class URLStrParserTest { }); } + @Test + void testDefault() { + URL url1 = URLStrParser.parseEncodedStr(URL.encode("dubbo://127.0.0.1?timeout=1234&default.timeout=5678")); + assertThat(url1.getParameter("timeout"), equalTo("1234")); + assertThat(url1.getParameter("default.timeout"), equalTo("5678")); + + URL url2 = URLStrParser.parseEncodedStr(URL.encode("dubbo://127.0.0.1?default.timeout=5678")); + assertThat(url2.getParameter("timeout"), equalTo("5678")); + assertThat(url2.getParameter("default.timeout"), equalTo("5678")); + } + } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java index 334ec39684..78c96e2891 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java @@ -59,6 +59,17 @@ class URLTest { assertEquals("org.apache.dubbo.config.RegistryConfig#0", url.getParameter("id")); } + @Test + void testDefault() { + URL url1 = URL.valueOf("dubbo://127.0.0.1:12345?timeout=1234&default.timeout=5678"); + assertEquals(1234, url1.getParameter("timeout", 0)); + assertEquals(5678, url1.getParameter("default.timeout", 0)); + + URL url2 = URL.valueOf("dubbo://127.0.0.1:12345?default.timeout=5678"); + assertEquals(5678, url2.getParameter("timeout", 0)); + assertEquals(5678, url2.getParameter("default.timeout", 0)); + } + @Test void test_valueOf_noProtocolAndHost() throws Exception { URL url = URL.valueOf("/context/path?version=1.0.0&application=morgan"); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/ClassUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/ClassUtilsTest.java index e2d990caf9..0e7c6b7a73 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/ClassUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/ClassUtilsTest.java @@ -60,22 +60,22 @@ class ClassUtilsTest { @Test void testForName2() { - ClassUtils.forName("boolean"); - ClassUtils.forName("byte"); - ClassUtils.forName("char"); - ClassUtils.forName("short"); - ClassUtils.forName("int"); - ClassUtils.forName("long"); - ClassUtils.forName("float"); - ClassUtils.forName("double"); - ClassUtils.forName("boolean[]"); - ClassUtils.forName("byte[]"); - ClassUtils.forName("char[]"); - ClassUtils.forName("short[]"); - ClassUtils.forName("int[]"); - ClassUtils.forName("long[]"); - ClassUtils.forName("float[]"); - ClassUtils.forName("double[]"); + Assertions.assertEquals(boolean.class, ClassUtils.forName("boolean")); + Assertions.assertEquals(byte.class, ClassUtils.forName("byte")); + Assertions.assertEquals(char.class, ClassUtils.forName("char")); + Assertions.assertEquals(short.class, ClassUtils.forName("short")); + Assertions.assertEquals(int.class, ClassUtils.forName("int")); + Assertions.assertEquals(long.class, ClassUtils.forName("long")); + Assertions.assertEquals(float.class, ClassUtils.forName("float")); + Assertions.assertEquals(double.class, ClassUtils.forName("double")); + Assertions.assertEquals(boolean[].class, ClassUtils.forName("boolean[]")); + Assertions.assertEquals(byte[].class, ClassUtils.forName("byte[]")); + Assertions.assertEquals(char[].class, ClassUtils.forName("char[]")); + Assertions.assertEquals(short[].class, ClassUtils.forName("short[]")); + Assertions.assertEquals(int[].class, ClassUtils.forName("int[]")); + Assertions.assertEquals(long[].class, ClassUtils.forName("long[]")); + Assertions.assertEquals(float[].class, ClassUtils.forName("float[]")); + Assertions.assertEquals(double[].class, ClassUtils.forName("double[]")); } @Test @@ -174,4 +174,4 @@ class ClassUtilsTest { } } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/EnvironmentTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/EnvironmentTest.java index 75c88306fc..5d502fffaa 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/EnvironmentTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/EnvironmentTest.java @@ -77,11 +77,11 @@ class EnvironmentTest { // test getConfigurationMaps(AbstractConfig config, String prefix) List> configurationMaps = environment.getConfigurationMaps(registryConfig, prefix); - Assertions.assertEquals(configurationMaps.size(), 7); + Assertions.assertEquals(7, configurationMaps.size()); // test getConfigurationMaps() configurationMaps = environment.getConfigurationMaps(); - Assertions.assertEquals(configurationMaps.size(), 6); + Assertions.assertEquals(6, configurationMaps.size()); CompositeConfiguration configuration1 = environment.getConfiguration(); CompositeConfiguration configuration2 = environment.getConfiguration(); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/DynamicConfigurationFactoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/DynamicConfigurationFactoryTest.java index f52b79e328..c5ef627379 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/DynamicConfigurationFactoryTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/DynamicConfigurationFactoryTest.java @@ -17,10 +17,11 @@ package org.apache.dubbo.common.config.configcenter; import org.apache.dubbo.common.config.configcenter.nop.NopDynamicConfigurationFactory; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.Test; -import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -36,4 +37,8 @@ class DynamicConfigurationFactoryTest { assertEquals(NopDynamicConfigurationFactory.class, factory.getClass()); assertEquals(NopDynamicConfigurationFactory.class, getExtensionLoader(DynamicConfigurationFactory.class).getExtension("nop").getClass()); } -} \ No newline at end of file + + private ExtensionLoader getExtensionLoader(Class extClass) { + return ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(extClass); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/convert/ConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/ConverterTest.java index 2deaf7ebb6..3a1407c2d6 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/convert/ConverterTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/ConverterTest.java @@ -16,13 +16,14 @@ */ package org.apache.dubbo.common.convert; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; @@ -60,4 +61,8 @@ class ConverterTest { assertEquals(Boolean.FALSE, converterUtil.convertIfPossible("false", Boolean.class)); assertEquals(Double.valueOf(1), converterUtil.convertIfPossible("1", Double.class)); } -} \ No newline at end of file + + private ExtensionLoader getExtensionLoader(Class extClass) { + return ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(extClass); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToLongConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToLongConverterTest.java index cda878d1c5..ad98ed4a95 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToLongConverterTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToLongConverterTest.java @@ -16,10 +16,12 @@ */ package org.apache.dubbo.common.convert; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.rpc.model.ApplicationModel; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -52,4 +54,8 @@ class StringToLongConverterTest { converter.convert("ttt"); }); } -} \ No newline at end of file + + private ExtensionLoader getExtensionLoader(Class extClass) { + return ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(extClass); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java index ff62b95ea7..4f8f1474f1 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java @@ -170,7 +170,7 @@ class ExtensionLoaderTest { // get origin instance from wrapper WrappedExt originImpl1 = impl1; while (originImpl1 instanceof WrappedExtWrapper) { - originImpl1 = ((WrappedExtWrapper)originImpl1).getOrigin(); + originImpl1 = ((WrappedExtWrapper) originImpl1).getOrigin(); } // test unwrapped instance @@ -292,7 +292,7 @@ class ExtensionLoaderTest { void test_getSupportedExtensions() { Set exts = getExtensionLoader(SimpleExt.class).getSupportedExtensions(); - Set expected = new HashSet(); + Set expected = new HashSet<>(); expected.add("impl1"); expected.add("impl2"); expected.add("impl3"); @@ -304,7 +304,7 @@ class ExtensionLoaderTest { void test_getSupportedExtensions_wrapperIsNotExt() { Set exts = getExtensionLoader(WrappedExt.class).getSupportedExtensions(); - Set expected = new HashSet(); + Set expected = new HashSet<>(); expected.add("impl1"); expected.add("impl2"); expected.add("impl3"); @@ -720,7 +720,7 @@ class ExtensionLoaderTest { void testDuplicatedImplWithoutOverriddenStrategy() { List loadingStrategies = ExtensionLoader.getLoadingStrategies(); ExtensionLoader.setLoadingStrategies(new DubboExternalLoadingStrategyTest(false), - new DubboInternalLoadingStrategyTest(false)); + new DubboInternalLoadingStrategyTest(false)); ExtensionLoader extensionLoader = getExtensionLoader(DuplicatedWithoutOverriddenExt.class); try { extensionLoader.getExtension("duplicated"); @@ -728,7 +728,7 @@ class ExtensionLoaderTest { } catch (IllegalStateException expected) { assertThat(expected.getMessage(), containsString("Failed to load extension class (interface: interface org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt")); assertThat(expected.getMessage(), containsString("cause: Duplicate extension org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt name duplicated")); - }finally { + } finally { //recover the loading strategies ExtensionLoader.setLoadingStrategies(loadingStrategies.toArray(new LoadingStrategy[loadingStrategies.size()])); } @@ -738,7 +738,7 @@ class ExtensionLoaderTest { void testDuplicatedImplWithOverriddenStrategy() { List loadingStrategies = ExtensionLoader.getLoadingStrategies(); ExtensionLoader.setLoadingStrategies(new DubboExternalLoadingStrategyTest(true), - new DubboInternalLoadingStrategyTest(true)); + new DubboInternalLoadingStrategyTest(true)); ExtensionLoader extensionLoader = getExtensionLoader(DuplicatedOverriddenExt.class); DuplicatedOverriddenExt duplicatedOverriddenExt = extensionLoader.getExtension("duplicated"); assertEquals("DuplicatedOverriddenExt1", duplicatedOverriddenExt.echo()); @@ -749,25 +749,25 @@ class ExtensionLoaderTest { @Test void testLoadByDubboInternalSPI() { ExtensionLoader extensionLoader = getExtensionLoader(SPI1.class); - SPI1 spi1 = extensionLoader.getExtension("1",true); + SPI1 spi1 = extensionLoader.getExtension("1", true); assertNotNull(spi1); ExtensionLoader extensionLoader2 = getExtensionLoader(SPI2.class); - SPI2 spi2 = extensionLoader2.getExtension("2",true); + SPI2 spi2 = extensionLoader2.getExtension("2", true); assertNotNull(spi2); try { ExtensionLoader extensionLoader3 = getExtensionLoader(SPI3.class); - SPI3 spi3 = extensionLoader3.getExtension("3",true); + SPI3 spi3 = extensionLoader3.getExtension("3", true); assertNotNull(spi3); - }catch (IllegalStateException illegalStateException){ + } catch (IllegalStateException illegalStateException) { if (!illegalStateException.getMessage().contains("No such extension")) { fail(); } } ExtensionLoader extensionLoader4 = getExtensionLoader(SPI4.class); - SPI4 spi4 = extensionLoader4.getExtension("4",true); + SPI4 spi4 = extensionLoader4.getExtension("4", true); assertNotNull(spi4); } @@ -780,7 +780,7 @@ class ExtensionLoaderTest { } /** - * The external {@link LoadingStrategy}, which can set if it support overridden + * The external {@link LoadingStrategy}, which can set if it supports overriding. */ private static class DubboExternalLoadingStrategyTest implements LoadingStrategy { @@ -832,4 +832,4 @@ class ExtensionLoaderTest { return MAX_PRIORITY; } } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoader_Compatible_Test.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoader_Compatible_Test.java index 967215037f..83596edb95 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoader_Compatible_Test.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoader_Compatible_Test.java @@ -19,6 +19,8 @@ package org.apache.dubbo.common.extension; import org.apache.dubbo.common.extension.compatible.CompatibleExt; import org.apache.dubbo.common.extension.compatible.impl.CompatibleExtImpl1; import org.apache.dubbo.common.extension.compatible.impl.CompatibleExtImpl2; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.Test; @@ -27,8 +29,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class ExtensionLoader_Compatible_Test { @Test - void test_getExtension() throws Exception { - assertTrue(ExtensionLoader.getExtensionLoader(CompatibleExt.class).getExtension("impl1") instanceof CompatibleExtImpl1); - assertTrue(ExtensionLoader.getExtensionLoader(CompatibleExt.class).getExtension("impl2") instanceof CompatibleExtImpl2); + void test_getExtension() { + + ModuleModel moduleModel = ApplicationModel.defaultModel().getDefaultModule(); + + assertTrue(moduleModel.getExtensionLoader(CompatibleExt.class).getExtension("impl1") instanceof CompatibleExtImpl1); + assertTrue(moduleModel.getExtensionLoader(CompatibleExt.class).getExtension("impl2") instanceof CompatibleExtImpl2); } -} \ No newline at end of file +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/ApplicationEvent.java b/dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Ageneric.java similarity index 67% rename from dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/ApplicationEvent.java rename to dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Ageneric.java index 07e762bf94..2662f029ab 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/ApplicationEvent.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Ageneric.java @@ -14,23 +14,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.apache.dubbo.common.model.person; -package org.apache.dubbo.metrics.event; +import java.io.Serializable; -public class ApplicationEvent extends MetricsEvent{ - private ApplicationEvent.Type type; +public class Ageneric implements Serializable { + public static String NAME = "A"; - public ApplicationEvent(Object source, ApplicationEvent.Type type) { - super(source); - this.type = type; + private String name = NAME; + private T data; + + public T getData() { + return data; } - public ApplicationEvent.Type getType() { - return type; + public void setData(T data) { + this.data = data; } - public void setType(ApplicationEvent.Type type) { - this.type = type; + public String getName() { + return name; } - } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Bgeneric.java b/dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Bgeneric.java new file mode 100644 index 0000000000..1a4408cbb6 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Bgeneric.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.model.person; + +import java.io.Serializable; + +public class Bgeneric implements Serializable { + public static String NAME = "B"; + + private String name = NAME; + private T data; + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public String getName() { + return name; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionConfig.java b/dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Cgeneric.java similarity index 55% rename from dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionConfig.java rename to dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Cgeneric.java index e9a9931c46..2a47cc50ad 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionConfig.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Cgeneric.java @@ -14,38 +14,43 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.rpc.protocol.rest.annotation.consumer; +package org.apache.dubbo.common.model.person; +import java.io.Serializable; -import org.apache.dubbo.common.serialize.Constants; +public class Cgeneric implements Serializable { + public static String NAME = "C"; -public class HttpConnectionConfig { + private String name = NAME; + private T data; + private Ageneric a; + private Bgeneric b; - - private int connectTimeout; - private int readTimeout; - private int chunkLength = 8196; - private byte serialization = Constants.FASTJSON2_SERIALIZATION_ID; - private int keepAlive = 60; - - - public int getConnectTimeout() { - return connectTimeout; + public T getData() { + return data; } - public int getReadTimeout() { - return readTimeout; + public void setData(T data) { + this.data = data; } - public int getChunkLength() { - return chunkLength; + public String getName() { + return name; } - public byte getSerialization() { - return serialization; + public Ageneric getA() { + return a; } - public int getKeepAlive() { - return keepAlive; + public void setA(Ageneric a) { + this.a = a; + } + + public Bgeneric getB() { + return b; + } + + public void setB(Bgeneric b) { + this.b = b; } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Dgeneric.java b/dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Dgeneric.java new file mode 100644 index 0000000000..0aaabd87a5 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Dgeneric.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.model.person; + +import java.io.Serializable; + +public class Dgeneric implements Serializable { + public static String NAME = "D"; + + private String name = NAME; + private T t; + private Y y; + private Z z; + + public T getT() { + return t; + } + + public void setT(T t) { + this.t = t; + } + + public Y getY() { + return y; + } + + public void setY(Y y) { + this.y = y; + } + + public Z getZ() { + return z; + } + + public void setZ(Z z) { + this.z = z; + } + + public String getName() { + return name; + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/status/reporter/FrameworkStatusReportServiceTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/status/reporter/FrameworkStatusReportServiceTest.java index f00a56cb5b..beb9e25fde 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/status/reporter/FrameworkStatusReportServiceTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/status/reporter/FrameworkStatusReportServiceTest.java @@ -75,13 +75,13 @@ class FrameworkStatusReportServiceTest { // verify registrationStatus Object registrationStatus = reportContent.get(REGISTRATION_STATUS); - Map registrationMap = JsonUtils.getJson().toJavaObject(String.valueOf(registrationStatus), Map.class); + Map registrationMap = JsonUtils.toJavaObject(String.valueOf(registrationStatus), Map.class); Assertions.assertEquals(registrationMap.get("application"), "APP"); Assertions.assertEquals(registrationMap.get("status"), "instance"); // verify addressConsumptionStatus Object addressConsumptionStatus = reportContent.get(ADDRESS_CONSUMPTION_STATUS); - Map consumptionMap = JsonUtils.getJson().toJavaObject(String.valueOf(addressConsumptionStatus), Map.class); + Map consumptionMap = JsonUtils.toJavaObject(String.valueOf(addressConsumptionStatus), Map.class); Assertions.assertEquals(consumptionMap.get("application"), "APP"); Assertions.assertEquals(consumptionMap.get("service"), "Test"); Assertions.assertEquals(consumptionMap.get("status"), "status"); @@ -91,7 +91,7 @@ class FrameworkStatusReportServiceTest { // verify migrationStepStatus Object migrationStepStatus = reportContent.get(MIGRATION_STEP_STATUS); - Map migrationStepStatusMap = JsonUtils.getJson().toJavaObject(String.valueOf(migrationStepStatus), Map.class); + Map migrationStepStatusMap = JsonUtils.toJavaObject(String.valueOf(migrationStepStatus), Map.class); Assertions.assertEquals(migrationStepStatusMap.get("originStep"), "FORCE_INTERFACE"); Assertions.assertEquals(migrationStepStatusMap.get("application"), "APP"); Assertions.assertEquals(migrationStepStatusMap.get("service"), "Test"); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactoryTest.java index 92cd977a3b..3d0e34cacc 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactoryTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactoryTest.java @@ -25,12 +25,8 @@ class NamedInternalThreadFactoryTest { @Test void newThread() throws Exception { NamedInternalThreadFactory namedInternalThreadFactory = new NamedInternalThreadFactory(); - Thread t = namedInternalThreadFactory.newThread(new Runnable() { - @Override - public void run() { + Thread t = namedInternalThreadFactory.newThread(() -> { }); - } - }); Assertions.assertEquals(t.getClass(), InternalThread.class, "thread is not InternalThread"); } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/MemorySafeLinkedBlockingQueueTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/MemorySafeLinkedBlockingQueueTest.java index e678cbd14b..41533eb494 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/MemorySafeLinkedBlockingQueueTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/MemorySafeLinkedBlockingQueueTest.java @@ -21,9 +21,12 @@ import org.apache.dubbo.common.concurrent.AbortPolicy; import org.apache.dubbo.common.concurrent.RejectException; import net.bytebuddy.agent.ByteBuddyAgent; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.lang.instrument.Instrumentation; +import java.util.concurrent.LinkedBlockingQueue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @@ -55,4 +58,60 @@ class MemorySafeLinkedBlockingQueueTest { assertThrows(RejectException.class, () -> queue.offer(() -> { })); } + + @Test + @Disabled("This test is not stable, it may fail due to performance (C1, C2)") + void testEfficiency() throws InterruptedException { + // if length is vert large(unit test may runs for a long time), so you may need to modify JVM param such as : -Xms=1024m -Xmx=2048m + // if you want to test efficiency of MemorySafeLinkedBlockingQueue, you may modify following param: length and times + int length = 1000, times = 1; + + // LinkedBlockingQueue insert Integer: 500W * 20 times + long spent1 = spend(new LinkedBlockingQueue<>(), length, times); + + // MemorySafeLinkedBlockingQueue insert Integer: 500W * 20 times + long spent2 = spend(newMemorySafeLinkedBlockingQueue(), length, times); + System.gc(); + + System.out.println(String.format("LinkedBlockingQueue spent %s millis, MemorySafeLinkedBlockingQueue spent %s millis", spent1, spent2)); + // efficiency between LinkedBlockingQueue and MemorySafeLinkedBlockingQueue is very nearly the same + Assertions.assertTrue(spent1 - spent2 <= 1); + } + + private static long spend(LinkedBlockingQueue lbq, int length, int times) throws InterruptedException { + // new Queue + if (lbq instanceof MemorySafeLinkedBlockingQueue) { + lbq = newMemorySafeLinkedBlockingQueue(); + } else { + lbq = new LinkedBlockingQueue<>(); + } + + long total = 0L; + for (int i = 0; i < times; i++) { + long start = System.currentTimeMillis(); + for (int j = 0; j < length; j++) { + lbq.offer(j); + } + long end = System.currentTimeMillis(); + long spent = end - start; + total += spent; + } + long result = total / times; + + // gc + System.gc(); + + return result; + } + + private static MemorySafeLinkedBlockingQueue newMemorySafeLinkedBlockingQueue() { + ByteBuddyAgent.install(); + final Instrumentation instrumentation = ByteBuddyAgent.getInstrumentation(); + final long objectSize = instrumentation.getObjectSize((Runnable) () -> { }); + int maxFreeMemory = (int) MemoryLimitCalculator.maxAvailable(); + MemorySafeLinkedBlockingQueue queue = new MemorySafeLinkedBlockingQueue<>(maxFreeMemory); + queue.setMaxFreeMemory((int) (MemoryLimitCalculator.maxAvailable() - objectSize)); + queue.setRejector(new AbortPolicy<>()); + return queue; + } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/ThreadlessExecutorTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/ThreadlessExecutorTest.java index fd806b9416..5643c3c422 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/ThreadlessExecutorTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/ThreadlessExecutorTest.java @@ -18,16 +18,12 @@ package org.apache.dubbo.common.threadpool; import org.apache.dubbo.common.URL; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.util.concurrent.CompletableFuture; - class ThreadlessExecutorTest { - private static ThreadlessExecutor executor; + private static final ThreadlessExecutor executor; static { - URL url = URL.valueOf("dubbo://127.0.0.1:12345"); executor = new ThreadlessExecutor(); } @@ -37,10 +33,6 @@ class ThreadlessExecutorTest { executor.execute(()->{throw new RuntimeException("test");}); } - CompletableFuture stubFuture = new CompletableFuture<>(); - executor.setWaitingFuture(stubFuture); - Assertions.assertEquals(executor.getWaitingFuture(),stubFuture); - executor.waitAndDrain(); executor.execute(()->{}); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutorTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutorTest.java index 68b3735ff7..03e90eddc1 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutorTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutorTest.java @@ -22,6 +22,7 @@ import org.apache.dubbo.common.threadpool.ThreadPool; import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.NamedThreadFactory; +import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; @@ -110,7 +111,7 @@ class EagerThreadPoolExecutorTest { long alive = 1000; //init queue and executor - TaskQueue taskQueue = new TaskQueue(queues); + TaskQueue taskQueue = new TaskQueue<>(queues); final EagerThreadPoolExecutor executor = new EagerThreadPoolExecutor(cores, threads, alive, @@ -163,9 +164,12 @@ class EagerThreadPoolExecutorTest { @Test void testSPI() { - ExecutorService executorService = (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class) + ExtensionLoader extensionLoader = ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(ThreadPool.class); + + ExecutorService executorService = (ExecutorService) extensionLoader .getExtension("eager") .getExecutor(URL); + Assertions.assertEquals("EagerThreadPoolExecutor", executorService.getClass() .getSimpleName(), "test spi fail!"); } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/url/URLParamTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/url/URLParamTest.java index d230dd7bcf..e881841152 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/url/URLParamTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/url/URLParamTest.java @@ -81,6 +81,33 @@ class URLParamTest { Assertions.assertEquals(urlParam3, URLParam.parse(urlParam3.getRawParam())); } + @Test + void testDefault() { + Map map = new HashMap<>(); + map.put("aaa", "aaa"); + map.put("bbb", "bbb"); + map.put("version", "2.0"); + map.put("timeout", "1234"); + map.put("default.timeout", "5678"); + + URLParam urlParam1 = URLParam.parse(map); + Assertions.assertEquals("1234", urlParam1.getParameter("timeout")); + Assertions.assertEquals("5678", urlParam1.getParameter("default.timeout")); + + map.remove("timeout"); + URLParam urlParam2 = URLParam.parse(map); + Assertions.assertEquals("5678", urlParam2.getParameter("timeout")); + Assertions.assertEquals("5678", urlParam2.getParameter("default.timeout")); + + URLParam urlParam3 = URLParam.parse("timeout=1234&default.timeout=5678"); + Assertions.assertEquals("1234", urlParam3.getParameter("timeout")); + Assertions.assertEquals("5678", urlParam3.getParameter("default.timeout")); + + URLParam urlParam4 = URLParam.parse("default.timeout=5678"); + Assertions.assertEquals("5678", urlParam4.getParameter("timeout")); + Assertions.assertEquals("5678", urlParam4.getParameter("default.timeout")); + } + @Test void testGetParameter() { URLParam urlParam1 = URLParam.parse("aaa=aaa&bbb&version=1.0&default.ccc=123"); @@ -270,4 +297,4 @@ class URLParamTest { Assertions.assertEquals("aaa",urlParam2.getAnyMethodParameter("method1")); Assertions.assertNull(urlParam2.getAnyMethodParameter("method2")); } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/JRETest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/JRETest.java index 6250e75964..ec5f34b1c0 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/JRETest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/JRETest.java @@ -21,6 +21,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import javax.lang.model.SourceVersion; + class JRETest { @@ -33,17 +35,10 @@ class JRETest { } @Test - @Disabled - void java8Version() { - JRE jre = JRE.currentVersion(); - Assertions.assertEquals(JRE.JAVA_8, jre); - } + void testCurrentVersion() { + // SourceVersion is an enum, which member name is RELEASE_XX. - @Test - @Disabled - void java19Version() { - JRE jre = JRE.currentVersion(); - Assertions.assertNotEquals(JRE.JAVA_19, jre); + Assertions.assertEquals(SourceVersion.latest().name().split("_")[1], + JRE.currentVersion().name().split("_")[1]); } - } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/JsonUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/JsonUtilsTest.java index 1f1c8ae99c..528ff02306 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/JsonUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/JsonUtilsTest.java @@ -112,7 +112,7 @@ class JsonUtilsTest { } @Test - public void consistentTest() { + void consistentTest() { List objs = new LinkedList<>(); { diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NamedThreadFactoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NamedThreadFactoryTest.java index a2dc5a84c9..57a0303134 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NamedThreadFactoryTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NamedThreadFactoryTest.java @@ -20,16 +20,23 @@ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import java.util.concurrent.atomic.AtomicInteger; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; class NamedThreadFactoryTest { + + private static final int INITIAL_THREAD_NUM = 1; + @Test - void testNewThread() throws Exception { + void testNewThread() { NamedThreadFactory factory = new NamedThreadFactory(); Thread t = factory.newThread(Mockito.mock(Runnable.class)); assertThat(t.getName(), allOf(containsString("pool-"), containsString("-thread-"))); @@ -39,10 +46,25 @@ class NamedThreadFactoryTest { } @Test - void testPrefixAndDaemon() throws Exception { + void testPrefixAndDaemon() { NamedThreadFactory factory = new NamedThreadFactory("prefix", true); Thread t = factory.newThread(Mockito.mock(Runnable.class)); assertThat(t.getName(), allOf(containsString("prefix-"), containsString("-thread-"))); assertTrue(t.isDaemon()); } -} \ No newline at end of file + + @Test + public void testGetThreadNum() { + NamedThreadFactory threadFactory = new NamedThreadFactory(); + AtomicInteger threadNum = threadFactory.getThreadNum(); + assertNotNull(threadNum); + assertEquals(INITIAL_THREAD_NUM, threadNum.get()); + } + + @Test + public void testGetThreadGroup() { + NamedThreadFactory threadFactory = new NamedThreadFactory(); + ThreadGroup threadGroup = threadFactory.getThreadGroup(); + assertNotNull(threadGroup); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java index 9140168ecd..4c67a41d99 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java @@ -34,11 +34,16 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import com.alibaba.fastjson.JSON; import org.apache.dubbo.common.model.Person; import org.apache.dubbo.common.model.SerializablePerson; import org.apache.dubbo.common.model.User; +import org.apache.dubbo.common.model.person.Bgeneric; import org.apache.dubbo.common.model.person.BigPerson; +import org.apache.dubbo.common.model.person.Cgeneric; +import org.apache.dubbo.common.model.person.Dgeneric; import org.apache.dubbo.common.model.person.FullAddress; +import org.apache.dubbo.common.model.person.Ageneric; import org.apache.dubbo.common.model.person.PersonInfo; import org.apache.dubbo.common.model.person.PersonMap; import org.apache.dubbo.common.model.person.PersonStatus; @@ -790,6 +795,209 @@ class PojoUtilsTest { assertEquals(PersonMap.class, result.getClass()); } + + protected PersonInfo createPersonInfoByName(String name) { + PersonInfo dataPerson = new PersonInfo(); + dataPerson.setName(name); + return dataPerson; + } + + protected Ageneric createAGenericPersonInfo(String name) { + Ageneric ret = new Ageneric(); + ret.setData(createPersonInfoByName(name)); + return ret; + } + + protected Bgeneric createBGenericPersonInfo(String name) { + Bgeneric ret = new Bgeneric(); + ret.setData(createPersonInfoByName(name)); + return ret; + } + + @Test + public void testPojoGeneric1() throws NoSuchMethodException { + String personName = "testName"; + + { + Ageneric genericPersonInfo = createAGenericPersonInfo(personName); + + Object o = JSON.toJSON(genericPersonInfo); + { + Ageneric personInfo = (Ageneric) PojoUtils.realize(o, Ageneric.class); + + assertEquals(Ageneric.NAME, personInfo.getName()); + assertTrue(personInfo.getData() instanceof Map); + } + { + Type[] createGenericPersonInfos = ReflectUtils.getReturnTypes(PojoUtilsTest.class.getDeclaredMethod("createAGenericPersonInfo", String.class)); + Ageneric personInfo = (Ageneric) PojoUtils.realize(o, (Class)createGenericPersonInfos[0], createGenericPersonInfos[1]); + + assertEquals(Ageneric.NAME, personInfo.getName()); + assertEquals(personInfo.getData().getClass(), PersonInfo.class); + assertEquals(personName, ((PersonInfo)personInfo.getData()).getName()); + } + } + { + Bgeneric genericPersonInfo = createBGenericPersonInfo(personName); + + Object o = JSON.toJSON(genericPersonInfo); + { + Bgeneric personInfo = (Bgeneric) PojoUtils.realize(o, Bgeneric.class); + + assertEquals(Bgeneric.NAME, personInfo.getName()); + assertTrue(personInfo.getData() instanceof Map); + } + { + Type[] createGenericPersonInfos = ReflectUtils.getReturnTypes(PojoUtilsTest.class.getDeclaredMethod("createBGenericPersonInfo", String.class)); + Bgeneric personInfo = (Bgeneric) PojoUtils.realize(o, (Class)createGenericPersonInfos[0], createGenericPersonInfos[1]); + + assertEquals(Bgeneric.NAME, personInfo.getName()); + assertEquals(personInfo.getData().getClass(), PersonInfo.class); + assertEquals(personName, ((PersonInfo)personInfo.getData()).getName()); + } + } + } + + protected Ageneric> createAGenericLoop(String name) { + Ageneric> ret = new Ageneric(); + ret.setData(createAGenericPersonInfo(name)); + return ret; + } + + protected Bgeneric> createBGenericWithAgeneric(String name) { + Bgeneric> ret = new Bgeneric(); + ret.setData(createAGenericPersonInfo(name)); + return ret; + } + + @Test + public void testPojoGeneric2() throws NoSuchMethodException { + String personName = "testName"; + + { + Ageneric> generic2PersonInfo = createAGenericLoop(personName); + Object o = JSON.toJSON(generic2PersonInfo); + { + Ageneric personInfo = (Ageneric) PojoUtils.realize(o, Ageneric.class); + + assertEquals(Ageneric.NAME, personInfo.getName()); + assertTrue(personInfo.getData() instanceof Map); + } + { + Type[] createGenericPersonInfos = ReflectUtils.getReturnTypes(PojoUtilsTest.class.getDeclaredMethod("createAGenericLoop", String.class)); + Ageneric personInfo = (Ageneric) PojoUtils.realize(o, (Class)createGenericPersonInfos[0], createGenericPersonInfos[1]); + + assertEquals(Ageneric.NAME, personInfo.getName()); + assertEquals(personInfo.getData().getClass(), Ageneric.class); + assertEquals(Ageneric.NAME, ((Ageneric)personInfo.getData()).getName()); + assertEquals(((Ageneric)personInfo.getData()).getData().getClass(), PersonInfo.class); + assertEquals(personName, ((PersonInfo)((Ageneric)personInfo.getData()).getData()).getName()); + } + } + { + + Bgeneric> generic = createBGenericWithAgeneric(personName); + Object o = JSON.toJSON(generic); + { + Ageneric personInfo = (Ageneric) PojoUtils.realize(o, Ageneric.class); + + assertEquals(Bgeneric.NAME, personInfo.getName()); + assertTrue(personInfo.getData() instanceof Map); + } + { + Type[] createGenericPersonInfos = ReflectUtils.getReturnTypes(PojoUtilsTest.class.getDeclaredMethod("createBGenericWithAgeneric", String.class)); + Bgeneric personInfo = (Bgeneric) PojoUtils.realize(o, (Class)createGenericPersonInfos[0], createGenericPersonInfos[1]); + + assertEquals(Bgeneric.NAME, personInfo.getName()); + assertEquals(personInfo.getData().getClass(), Ageneric.class); + assertEquals(Ageneric.NAME, ((Ageneric)personInfo.getData()).getName()); + assertEquals(((Ageneric)personInfo.getData()).getData().getClass(), PersonInfo.class); + assertEquals(personName, ((PersonInfo)((Ageneric)personInfo.getData()).getData()).getName()); + } + } + } + + protected Cgeneric createCGenericPersonInfo(String name) { + Cgeneric ret = new Cgeneric(); + ret.setData(createPersonInfoByName(name)); + ret.setA(createAGenericPersonInfo(name)); + ret.setB(createBGenericPersonInfo(name)); + return ret; + } + + @Test + public void testPojoGeneric3() throws NoSuchMethodException { + String personName = "testName"; + + Cgeneric generic = createCGenericPersonInfo(personName); + Object o = JSON.toJSON(generic); + { + Cgeneric personInfo = (Cgeneric) PojoUtils.realize(o, Cgeneric.class); + + assertEquals(Cgeneric.NAME, personInfo.getName()); + assertTrue(personInfo.getData() instanceof Map); + assertTrue(personInfo.getA().getData() instanceof Map); + assertTrue(personInfo.getB().getData() instanceof PersonInfo); + + } + { + Type[] createGenericPersonInfos = ReflectUtils.getReturnTypes(PojoUtilsTest.class.getDeclaredMethod("createCGenericPersonInfo", String.class)); + Cgeneric personInfo = (Cgeneric) PojoUtils.realize(o, (Class)createGenericPersonInfos[0], createGenericPersonInfos[1]); + + assertEquals(Cgeneric.NAME, personInfo.getName()); + assertEquals(personInfo.getData().getClass(), PersonInfo.class); + assertEquals(personName, ((PersonInfo)personInfo.getData()).getName()); + + assertEquals(personInfo.getA().getClass(), Ageneric.class); + assertEquals(personInfo.getA().getData().getClass(), PersonInfo.class); + assertEquals(personInfo.getB().getClass(), Bgeneric.class); + assertEquals(personInfo.getB().getData().getClass(), PersonInfo.class); + } + } + + protected Dgeneric, Bgeneric, Cgeneric> createDGenericPersonInfo(String name) { + Dgeneric, Bgeneric, Cgeneric> ret = new Dgeneric(); + ret.setT(createAGenericPersonInfo(name)); + ret.setY(createBGenericPersonInfo(name)); + ret.setZ(createCGenericPersonInfo(name)); + return ret; + } + + @Test + public void testPojoGeneric4() throws NoSuchMethodException { + String personName = "testName"; + + Dgeneric generic = createDGenericPersonInfo(personName); + Object o = JSON.toJSON(generic); + { + Dgeneric personInfo = (Dgeneric) PojoUtils.realize(o, Dgeneric.class); + + assertEquals(Dgeneric.NAME, personInfo.getName()); + assertTrue(personInfo.getT() instanceof Map); + assertTrue(personInfo.getY() instanceof Map); + assertTrue(personInfo.getZ() instanceof Map); + } + { + Type[] createGenericPersonInfos = ReflectUtils.getReturnTypes(PojoUtilsTest.class.getDeclaredMethod("createDGenericPersonInfo", String.class)); + Dgeneric personInfo = (Dgeneric) PojoUtils.realize(o, (Class)createGenericPersonInfos[0], createGenericPersonInfos[1]); + + assertEquals(Dgeneric.NAME, personInfo.getName()); + + assertEquals(personInfo.getT().getClass(), Ageneric.class); + assertEquals(((Ageneric)personInfo.getT()).getData().getClass(), PersonInfo.class); + assertEquals(personInfo.getY().getClass(), Bgeneric.class); + assertEquals(((Bgeneric)personInfo.getY()).getData().getClass(), PersonInfo.class); + assertEquals(personInfo.getZ().getClass(), Cgeneric.class); + assertEquals(((Cgeneric)personInfo.getZ()).getData().getClass(), PersonInfo.class); + + assertEquals(personInfo.getZ().getClass(), Cgeneric.class); + assertEquals(((Cgeneric)personInfo.getZ()).getA().getClass(), Ageneric.class); + assertEquals(((Cgeneric)personInfo.getZ()).getA().getData().getClass(), PersonInfo.class); + assertEquals(((Cgeneric)personInfo.getZ()).getB().getClass(), Bgeneric.class); + assertEquals(((Cgeneric)personInfo.getZ()).getB().getData().getClass(), PersonInfo.class); + } + } + public enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java index 15257f253d..2da5fabfb1 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Assertions; @@ -77,7 +78,7 @@ class ReflectUtilsTest { } @Test - void testIsCompatible() throws Exception { + void testIsCompatible() { assertTrue(ReflectUtils.isCompatible(short.class, (short) 1)); assertTrue(ReflectUtils.isCompatible(int.class, 1)); assertTrue(ReflectUtils.isCompatible(double.class, 1.2)); @@ -86,36 +87,45 @@ class ReflectUtilsTest { } @Test - void testIsCompatibleWithArray() throws Exception { + void testIsCompatibleWithArray() { assertFalse(ReflectUtils.isCompatible(new Class[]{short.class, int.class}, new Object[]{(short) 1})); assertFalse(ReflectUtils.isCompatible(new Class[]{double.class}, new Object[]{"hello"})); assertTrue(ReflectUtils.isCompatible(new Class[]{double.class}, new Object[]{1.2})); } @Test - void testGetCodeBase() throws Exception { + void testGetCodeBase() { assertNull(ReflectUtils.getCodeBase(null)); assertNull(ReflectUtils.getCodeBase(String.class)); assertNotNull(ReflectUtils.getCodeBase(ReflectUtils.class)); } @Test - void testGetName() throws Exception { + void testGetName() { // getName assertEquals("boolean", ReflectUtils.getName(boolean.class)); assertEquals("int[][][]", ReflectUtils.getName(int[][][].class)); assertEquals("java.lang.Object[][]", ReflectUtils.getName(Object[][].class)); + } + @Test + void testGetDesc() { // getDesc assertEquals("Z", ReflectUtils.getDesc(boolean.class)); assertEquals("[[[I", ReflectUtils.getDesc(int[][][].class)); assertEquals("[[Ljava/lang/Object;", ReflectUtils.getDesc(Object[][].class)); + } + @Test + void testName2desc() { // name2desc assertEquals("Z", ReflectUtils.name2desc(ReflectUtils.getName(boolean.class))); assertEquals("[[[I", ReflectUtils.name2desc(ReflectUtils.getName(int[][][].class))); assertEquals("[[Ljava/lang/Object;", ReflectUtils.name2desc(ReflectUtils.getName(Object[][].class))); + } + @Test + void testDesc2name() { // desc2name assertEquals("short[]", ReflectUtils.desc2name(ReflectUtils.getDesc(short[].class))); assertEquals("boolean[]", ReflectUtils.desc2name(ReflectUtils.getDesc(boolean[].class))); @@ -131,12 +141,12 @@ class ReflectUtilsTest { } @Test - void testGetGenericClass() throws Exception { + void testGetGenericClass() { assertThat(ReflectUtils.getGenericClass(Foo1.class), sameInstance(String.class)); } @Test - void testGetGenericClassWithIndex() throws Exception { + void testGetGenericClassWithIndex() { assertThat(ReflectUtils.getGenericClass(Foo1.class, 0), sameInstance(String.class)); assertThat(ReflectUtils.getGenericClass(Foo1.class, 1), sameInstance(Integer.class)); assertThat(ReflectUtils.getGenericClass(Foo2.class, 0), sameInstance(List.class)); @@ -148,7 +158,7 @@ class ReflectUtilsTest { @Test void testGetMethodName() throws Exception { assertThat(ReflectUtils.getName(Foo2.class.getDeclaredMethod("hello", int[].class)), - equalTo("java.util.List hello(int[])")); + equalTo("java.util.List hello(int[])")); } @Test @@ -158,7 +168,7 @@ class ReflectUtilsTest { } @Test - void testGetConstructorName() throws Exception { + void testGetConstructorName() { Constructor c = Foo2.class.getConstructors()[0]; assertThat(ReflectUtils.getName(c), equalTo("(java.util.List,int[])")); } @@ -174,18 +184,18 @@ class ReflectUtilsTest { @Test void testGetDescMethod() throws Exception { assertThat(ReflectUtils.getDesc(Foo2.class.getDeclaredMethod("hello", int[].class)), - equalTo("hello([I)Ljava/util/List;")); + equalTo("hello([I)Ljava/util/List;")); } @Test - void testGetDescConstructor() throws Exception { + void testGetDescConstructor() { assertThat(ReflectUtils.getDesc(Foo2.class.getConstructors()[0]), equalTo("(Ljava/util/List;[I)V")); } @Test void testGetDescWithoutMethodName() throws Exception { assertThat(ReflectUtils.getDescWithoutMethodName(Foo2.class.getDeclaredMethod("hello", int[].class)), - equalTo("([I)Ljava/util/List;")); + equalTo("([I)Ljava/util/List;")); } @Test @@ -208,7 +218,7 @@ class ReflectUtilsTest { } @Test - void testIsInstance() throws Exception { + void testIsInstance() { assertTrue(ReflectUtils.isInstance(new Foo1(), Foo.class.getName())); } @@ -227,9 +237,9 @@ class ReflectUtilsTest { @Test void testGetPropertyNameFromBeanReadMethod() throws Exception { Method method = EmptyClass.class.getMethod("getProperty"); - assertEquals(ReflectUtils.getPropertyNameFromBeanReadMethod(method), "property"); + assertEquals("property", ReflectUtils.getPropertyNameFromBeanReadMethod(method)); method = EmptyClass.class.getMethod("isSet"); - assertEquals(ReflectUtils.getPropertyNameFromBeanReadMethod(method), "set"); + assertEquals("set", ReflectUtils.getPropertyNameFromBeanReadMethod(method)); } @Test @@ -243,7 +253,7 @@ class ReflectUtilsTest { @Test void testGetPropertyNameFromBeanWriteMethod() throws Exception { Method method = EmptyClass.class.getMethod("setProperty", EmptyProperty.class); - assertEquals(ReflectUtils.getPropertyNameFromBeanWriteMethod(method), "property"); + assertEquals("property", ReflectUtils.getPropertyNameFromBeanWriteMethod(method)); } @Test @@ -255,7 +265,7 @@ class ReflectUtilsTest { } @Test - void testGetBeanPropertyFields() throws Exception { + void testGetBeanPropertyFields() { Map map = ReflectUtils.getBeanPropertyFields(EmptyClass.class); assertThat(map.size(), is(2)); assertThat(map, hasKey("set")); @@ -268,7 +278,7 @@ class ReflectUtilsTest { } @Test - void testGetBeanPropertyReadMethods() throws Exception { + void testGetBeanPropertyReadMethods() { Map map = ReflectUtils.getBeanPropertyReadMethods(EmptyClass.class); assertThat(map.size(), is(2)); assertThat(map, hasKey("set")); @@ -332,7 +342,7 @@ class ReflectUtilsTest { void testFindMethodByMethodSignature_override() throws Exception { { Method m = ReflectUtils.findMethodByMethodSignature(TestedClass.class, - "overrideMethod", new String[]{"int"}); + "overrideMethod", new String[]{"int"}); assertEquals("overrideMethod", m.getName()); Class[] parameterTypes = m.getParameterTypes(); @@ -341,7 +351,7 @@ class ReflectUtilsTest { } { Method m = ReflectUtils.findMethodByMethodSignature(TestedClass.class, - "overrideMethod", new String[]{"java.lang.Integer"}); + "overrideMethod", new String[]{"java.lang.Integer"}); assertEquals("overrideMethod", m.getName()); Class[] parameterTypes = m.getParameterTypes(); @@ -363,7 +373,7 @@ class ReflectUtilsTest { @Test void testFindMethodByMethodSignatureNotFound() throws Exception { try { - ReflectUtils.findMethodByMethodSignature(TestedClass.class, "notExsited", null); + ReflectUtils.findMethodByMethodSignature(TestedClass.class, "doesNotExist", null); fail(); } catch (NoSuchMethodException expected) { assertThat(expected.getMessage(), containsString("No such method ")); @@ -372,28 +382,30 @@ class ReflectUtilsTest { } @Test - void testGetEmptyObject() throws Exception { + void testGetEmptyObject() { assertTrue(ReflectUtils.getEmptyObject(Collection.class) instanceof Collection); assertTrue(ReflectUtils.getEmptyObject(List.class) instanceof List); assertTrue(ReflectUtils.getEmptyObject(Set.class) instanceof Set); assertTrue(ReflectUtils.getEmptyObject(Map.class) instanceof Map); assertTrue(ReflectUtils.getEmptyObject(Object[].class) instanceof Object[]); - assertEquals(ReflectUtils.getEmptyObject(String.class), ""); - assertEquals(ReflectUtils.getEmptyObject(short.class), (short) 0); - assertEquals(ReflectUtils.getEmptyObject(byte.class), (byte) 0); - assertEquals(ReflectUtils.getEmptyObject(int.class), 0); - assertEquals(ReflectUtils.getEmptyObject(long.class), 0L); - assertEquals(ReflectUtils.getEmptyObject(float.class), (float) 0); - assertEquals(ReflectUtils.getEmptyObject(double.class), (double) 0); - assertEquals(ReflectUtils.getEmptyObject(char.class), '\0'); - assertEquals(ReflectUtils.getEmptyObject(boolean.class), Boolean.FALSE); + + assertEquals("", ReflectUtils.getEmptyObject(String.class)); + assertEquals((short) 0, ReflectUtils.getEmptyObject(short.class)); + assertEquals((byte) 0, ReflectUtils.getEmptyObject(byte.class)); + assertEquals(0, ReflectUtils.getEmptyObject(int.class)); + assertEquals(0L, ReflectUtils.getEmptyObject(long.class)); + assertEquals((float) 0, ReflectUtils.getEmptyObject(float.class)); + assertEquals((double) 0, ReflectUtils.getEmptyObject(double.class)); + assertEquals('\0', ReflectUtils.getEmptyObject(char.class)); + assertEquals(Boolean.FALSE, ReflectUtils.getEmptyObject(boolean.class)); + EmptyClass object = (EmptyClass) ReflectUtils.getEmptyObject(EmptyClass.class); assertNotNull(object); assertNotNull(object.getProperty()); } @Test - void testForName1() throws Exception { + void testForName1() { assertThat(ReflectUtils.forName(ReflectUtils.class.getName()), sameInstance(ReflectUtils.class)); } @@ -405,8 +417,8 @@ class ReflectUtilsTest { } @Test - void testGetReturnTypes () throws Exception{ - Class clazz = TypeClass.class; + void testGetReturnTypes() throws Exception { + Class clazz = TypeClass.class; Type[] types = ReflectUtils.getReturnTypes(clazz.getMethod("getFuture")); Assertions.assertEquals("java.lang.String", types[0].getTypeName()); @@ -542,6 +554,4 @@ class ReflectUtilsTest { return null; } } - - } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/UrlUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/UrlUtilsTest.java index b82be418f6..71d10302b8 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/UrlUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/UrlUtilsTest.java @@ -20,6 +20,9 @@ import org.apache.dubbo.common.URL; import org.junit.jupiter.api.Test; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -32,15 +35,16 @@ import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; class UrlUtilsTest { String localAddress = "127.0.0.1"; + + + + @Test void testAddressNull() { String exceptionMessage = "Address is not allowed to be empty, please re-enter."; @@ -384,6 +388,175 @@ class UrlUtilsTest { assertTrue(UrlUtils.isMatch(consumerUrl, url)); URL consumerUrl1 = URL.valueOf("consumer://127.0.0.1/com.xxx.XxxService?default.version=1.0.0&default.group=test"); - assertTrue(UrlUtils.isMatch(consumerUrl, url)); + assertTrue(UrlUtils.isMatch(consumerUrl1, url)); } -} \ No newline at end of file + + @Test + public void testIsConsumer() { + String address1 = "remote://root:alibaba@127.0.0.1:9090"; + URL url1 = UrlUtils.parseURL(address1, null); + String address2 = "consumer://root:alibaba@127.0.0.1:9090"; + URL url2 = UrlUtils.parseURL(address2, null); + String address3 = "consumer://root:alibaba@127.0.0.1"; + URL url3 = UrlUtils.parseURL(address3, null); + + assertFalse(UrlUtils.isConsumer(url1)); + assertTrue(UrlUtils.isConsumer(url2)); + assertTrue(UrlUtils.isConsumer(url3)); + + } + + @Test + public void testPrivateConstructor() throws Exception { + Constructor constructor = UrlUtils.class.getDeclaredConstructor(); + assertTrue(Modifier.isPrivate(constructor.getModifiers())); + constructor.setAccessible(true); + assertThrows(InvocationTargetException.class, () -> { + constructor.newInstance(); + }); + } + + + @Test + public void testClassifyUrls() { + + String address1 = "remote://root:alibaba@127.0.0.1:9090"; + URL url1 = UrlUtils.parseURL(address1, null); + String address2 = "consumer://root:alibaba@127.0.0.1:9090"; + URL url2 = UrlUtils.parseURL(address2, null); + String address3 = "remote://root:alibaba@127.0.0.1"; + URL url3 = UrlUtils.parseURL(address3, null); + String address4 = "consumer://root:alibaba@127.0.0.1"; + URL url4 = UrlUtils.parseURL(address4, null); + + List urls = new ArrayList<>(); + urls.add(url1); + urls.add(url2); + urls.add(url3); + urls.add(url4); + + List consumerUrls = UrlUtils.classifyUrls(urls, UrlUtils::isConsumer); + assertEquals(2, consumerUrls.size()); + assertTrue(consumerUrls.contains(url2)); + assertTrue(consumerUrls.contains(url4)); + + List nonConsumerUrls = UrlUtils.classifyUrls(urls, url -> !UrlUtils.isConsumer(url)); + assertEquals(2, nonConsumerUrls.size()); + assertTrue(nonConsumerUrls.contains(url1)); + assertTrue(nonConsumerUrls.contains(url3)); + } + + @Test + public void testHasServiceDiscoveryRegistryProtocol() { + String address1 = "http://root:alibaba@127.0.0.1:9090/dubbo.test.api"; + URL url1 = UrlUtils.parseURL(address1, null); + String address2 = "service-discovery-registry://root:alibaba@127.0.0.1:9090/dubbo.test.api"; + URL url2 = UrlUtils.parseURL(address2, null); + + assertFalse(UrlUtils.hasServiceDiscoveryRegistryProtocol(url1)); + assertTrue(UrlUtils.hasServiceDiscoveryRegistryProtocol(url2)); + } + + + private static final String SERVICE_REGISTRY_TYPE = "service"; + private static final String REGISTRY_TYPE_KEY = "registry-type"; + + @Test + public void testHasServiceDiscoveryRegistryTypeKey() { + Map parameters1 = new HashMap<>(); + parameters1.put(REGISTRY_TYPE_KEY, "value2"); + assertFalse(UrlUtils.hasServiceDiscoveryRegistryTypeKey(parameters1)); + + Map parameters2 = new HashMap<>(); + parameters2.put(REGISTRY_TYPE_KEY, SERVICE_REGISTRY_TYPE); + + assertTrue(UrlUtils.hasServiceDiscoveryRegistryTypeKey(parameters2)); + } + + @Test + public void testIsConfigurator() { + String address1 = "http://example.com"; + URL url1 = UrlUtils.parseURL(address1, null); + String address2 = "override://example.com"; + URL url2 = UrlUtils.parseURL(address2, null); + String address3 = "http://example.com?category=configurators"; + URL url3 = UrlUtils.parseURL(address3, null); + + assertFalse(UrlUtils.isConfigurator(url1)); + assertTrue(UrlUtils.isConfigurator(url2)); + assertTrue(UrlUtils.isConfigurator(url3)); + } + + @Test + public void testIsRoute() { + String address1 = "http://example.com"; + URL url1 = UrlUtils.parseURL(address1, null); + String address2 = "route://example.com"; + URL url2 = UrlUtils.parseURL(address2, null); + String address3 = "http://example.com?category=routers"; + URL url3 = UrlUtils.parseURL(address3, null); + + assertFalse(UrlUtils.isRoute(url1)); + assertTrue(UrlUtils.isRoute(url2)); + assertTrue(UrlUtils.isRoute(url3)); + } + + @Test + public void testIsProvider() { + String address1 = "http://example.com"; + URL url1 = UrlUtils.parseURL(address1, null); + String address2 = "override://example.com"; + URL url2 = UrlUtils.parseURL(address2, null); + String address3 = "route://example.com"; + URL url3 = UrlUtils.parseURL(address3, null); + String address4 = "http://example.com?category=providers"; + URL url4 = UrlUtils.parseURL(address4, null); + String address5 = "http://example.com?category=something-else"; + URL url5 = UrlUtils.parseURL(address5, null); + + + assertTrue(UrlUtils.isProvider(url1)); + assertFalse(UrlUtils.isProvider(url2)); + assertFalse(UrlUtils.isProvider(url3)); + assertTrue(UrlUtils.isProvider(url4)); + assertFalse(UrlUtils.isProvider(url5)); + } + + + @Test + public void testIsRegistry() { + String address1 = "http://example.com"; + URL url1 = UrlUtils.parseURL(address1, null); + String address2 = "registry://example.com"; + URL url2 = UrlUtils.parseURL(address2, null); + String address3 = "sr://example.com"; + URL url3 = UrlUtils.parseURL(address3, null); + String address4 = "custom-registry-protocol://example.com"; + URL url4 = UrlUtils.parseURL(address4, null); + + assertFalse(UrlUtils.isRegistry(url1)); + assertTrue(UrlUtils.isRegistry(url2)); + assertFalse(UrlUtils.isRegistry(url3)); + assertTrue(UrlUtils.isRegistry(url4)); + } + + + + @Test + public void testIsServiceDiscoveryURL() { + String address1 = "http://example.com"; + URL url1 = UrlUtils.parseURL(address1, null); + String address2 = "service-discovery-registry://example.com"; + URL url2 = UrlUtils.parseURL(address2, null); + String address3 = "SERVICE-DISCOVERY-REGISTRY://example.com"; + URL url3 = UrlUtils.parseURL(address3, null); + String address4 = "http://example.com?registry-type=service"; + URL url4 = UrlUtils.parseURL(address4, null); + url4.addParameter(REGISTRY_TYPE_KEY, SERVICE_REGISTRY_TYPE); + + assertFalse(UrlUtils.isServiceDiscoveryURL(url1)); + assertTrue(UrlUtils.isServiceDiscoveryURL(url2)); + assertTrue(UrlUtils.isServiceDiscoveryURL(url3)); + assertTrue(UrlUtils.isServiceDiscoveryURL(url4)); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/version/VersionTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/version/VersionTest.java index 0c6604e74d..f9781e4471 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/version/VersionTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/version/VersionTest.java @@ -22,6 +22,12 @@ import org.apache.dubbo.common.Version; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.util.Enumeration; + class VersionTest { @Test @@ -93,12 +99,62 @@ class VersionTest { } @Test - void testGetVersion() { - Assertions.assertEquals("1.0.0", Version.getVersion()); + void testGetVersion() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class versionClass = reloadVersionClass(); + Assertions.assertEquals("1.0.0", versionClass.getDeclaredMethod("getVersion").invoke(null)); + } + + private static Class reloadVersionClass() throws ClassNotFoundException { + ClassLoader originClassLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader classLoader = new ClassLoader(originClassLoader) { + @Override + public Class loadClass(String name) throws ClassNotFoundException { + if (name.equals("org.apache.dubbo.common.Version")) { + return findClass(name); + } + return super.loadClass(name); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + try { + byte[] bytes = loadClassData(name); + return defineClass(name, bytes, 0, bytes.length); + } catch (Exception e) { + return getParent().loadClass(name); + } + } + + + public byte[] loadClassData(String className) throws IOException { + className = className.replaceAll("\\.", "/"); + String path = Version.class.getProtectionDomain().getCodeSource().getLocation().getPath() + className + ".class"; + FileInputStream fileInputStream; + byte[] classBytes; + fileInputStream = new FileInputStream(path); + int length = fileInputStream.available(); + classBytes = new byte[length]; + fileInputStream.read(classBytes); + fileInputStream.close(); + return classBytes; + } + + @Override + public Enumeration getResources(String name) throws IOException { + + if (name.equals("META-INF/versions/dubbo-common")) { + return super.getResources("META-INF/test-versions/dubbo-common"); + } + return super.getResources(name); + } + }; + Class versionClass = classLoader.loadClass("org.apache.dubbo.common.Version"); + return versionClass; } @Test - void testGetLastCommitId() { - Assertions.assertEquals("82a29fcd674216fe9bea10b6efef3196929dd7ca", Version.getLastCommitId()); + void testGetLastCommitId() throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException { + Class versionClass = reloadVersionClass(); + Assertions.assertEquals("82a29fcd674216fe9bea10b6efef3196929dd7ca", versionClass.getDeclaredMethod("getLastCommitId").invoke(null)); } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/vo/UserVo.java b/dubbo-common/src/test/java/org/apache/dubbo/common/vo/UserVo.java new file mode 100644 index 0000000000..336a0562ad --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/vo/UserVo.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.dubbo.common.vo; + +import java.util.Objects; + +public class UserVo { + private String name; + private String addr; + private int age; + + public UserVo(String name, String addr, int age) { + this.name = name; + this.addr = addr; + this.age = age; + } + + public UserVo() { + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddr() { + return addr; + } + + public void setAddr(String addr) { + this.addr = addr; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public static UserVo getInstance() { + return new UserVo("dubbo", "hangzhou", 10); + } + + @Override + public String toString() { + return "UserVo{" + + "name='" + name + '\'' + + ", addr='" + addr + '\'' + + ", age=" + age + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + UserVo userVo = (UserVo) o; + return age == userVo.age && Objects.equals(name, userVo.name) && Objects.equals(addr, userVo.addr); + } + + @Override + public int hashCode() { + return Objects.hash(name, addr, age); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/config/AbstractInterfaceConfigTest.java b/dubbo-common/src/test/java/org/apache/dubbo/config/AbstractInterfaceConfigTest.java index a09ea89e21..44a330aad1 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/config/AbstractInterfaceConfigTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/config/AbstractInterfaceConfigTest.java @@ -18,7 +18,6 @@ package org.apache.dubbo.config; import org.apache.dubbo.common.constants.CommonConstants; - import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -236,7 +235,28 @@ class AbstractInterfaceConfigTest { Assertions.assertEquals("scope", interfaceConfig.getScope()); } + @Test + void testVerifyMethod() { + InterfaceConfig2 interfaceConfig2 = new InterfaceConfig2(); + MethodConfig methodConfig = new MethodConfig(); + methodConfig.setTimeout(5000); + methodConfig.setName("sayHello"); + Class clazz = Greeting.class; + boolean verifyResult = interfaceConfig2.verifyMethodConfig(methodConfig, clazz, false); + Assertions.assertTrue(verifyResult); + + boolean verifyResult2 = interfaceConfig2.verifyMethodConfig(methodConfig, clazz, true); + Assertions.assertFalse(verifyResult2); + } + + public static class InterfaceConfig2 extends AbstractInterfaceConfig { + @Override + protected boolean isNeedCheckMethod() { + return false; + } + } + public static class InterfaceConfig extends AbstractInterfaceConfig { } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/config/context/ConfigManagerTest.java b/dubbo-common/src/test/java/org/apache/dubbo/config/context/ConfigManagerTest.java index 95f3e2679e..5ddd74aad4 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/config/context/ConfigManagerTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/config/context/ConfigManagerTest.java @@ -329,6 +329,7 @@ class ConfigManagerTest { applicationConfig22.setParameters(CollectionUtils.toStringMap("k1", "v11", "k2", "v22", "k3", "v3")); configManager.addConfig(applicationConfig11); configManager.addConfig(applicationConfig22); + assertEquals(applicationConfig11, configManager.getApplicationOrElseThrow()); assertEquals(applicationConfig11.getName(), "app22"); assertEquals(applicationConfig11.getParameters(), CollectionUtils.toStringMap("k1", "v11", "k2", "v22", "k3", "v3")); @@ -345,9 +346,10 @@ class ConfigManagerTest { applicationConfig44.setParameters(CollectionUtils.toStringMap("k1", "v11", "k2", "v22", "k3", "v3")); configManager.addConfig(applicationConfig33); configManager.addConfig(applicationConfig44); + assertEquals(applicationConfig33, configManager.getApplicationOrElseThrow()); - assertEquals(applicationConfig33.getName(), "app33"); - assertEquals(applicationConfig33.getParameters(), CollectionUtils.toStringMap("k1", "v1", "k2", "v2", "k3", "v3")); + assertEquals("app33", applicationConfig33.getName()); + assertEquals(CollectionUtils.toStringMap("k1", "v1", "k2", "v2", "k3", "v3"), applicationConfig33.getParameters()); } finally { System.clearProperty(DUBBO_CONFIG_MODE); } @@ -359,12 +361,22 @@ class ConfigManagerTest { registryConfig.setId("registryID_1"); configManager.addRegistry(registryConfig); Optional registryConfigOptional = configManager.getConfig(RegistryConfig.class, registryConfig.getId()); - Assertions.assertEquals(registryConfigOptional.get(), registryConfig); + + if (registryConfigOptional.isPresent()) { + Assertions.assertEquals(registryConfigOptional.get(), registryConfig); + } else { + fail("registryConfigOptional is empty! "); + } ProtocolConfig protocolConfig = new ProtocolConfig("dubbo"); configManager.addProtocol(protocolConfig); Optional protocolConfigOptional = configManager.getConfig(ProtocolConfig.class, protocolConfig.getName()); - Assertions.assertEquals(protocolConfigOptional.get(), protocolConfig); + + if (protocolConfigOptional.isPresent()) { + Assertions.assertEquals(protocolConfigOptional.get(), protocolConfig); + } else { + fail("protocolConfigOptional is empty! "); + } // test multi config has same name(dubbo) ProtocolConfig protocolConfig2 = new ProtocolConfig("dubbo"); @@ -384,7 +396,7 @@ class ConfigManagerTest { moduleConfig.setId("moduleID_1"); moduleConfigManager.setModule(moduleConfig); Optional moduleConfigOptional = moduleConfigManager.getConfig(ModuleConfig.class, moduleConfig.getId()); - Assertions.assertEquals(moduleConfigOptional.get(), moduleConfig); + Assertions.assertEquals(moduleConfig, moduleConfigOptional.get()); Optional config = moduleConfigManager.getConfig(RegistryConfig.class, registryConfig.getId()); Assertions.assertEquals(config.get(), registryConfig); @@ -405,7 +417,7 @@ class ConfigManagerTest { System.setProperty("dubbo.protocols.rest2.port", "8081"); configManager.loadConfigsOfTypeFromProps(ProtocolConfig.class); Collection protocols = configManager.getProtocols(); - Assertions.assertEquals(protocols.size(), 4); + Assertions.assertEquals(4, protocols.size()); System.setProperty("dubbo.applications.app1.name", "app-demo1"); System.setProperty("dubbo.applications.app2.name", "app-demo2"); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/MetadataTest.java b/dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/MetadataTest.java index d06044c504..d66136ad1c 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/MetadataTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/MetadataTest.java @@ -49,13 +49,13 @@ class MetadataTest { void testInnerClassType() { TypeDefinitionBuilder builder = new TypeDefinitionBuilder(); TypeDefinition td = builder.build(OuterClass.InnerClass.class, OuterClass.InnerClass.class); - System.out.println(">> testInnerClassType: " + JsonUtils.getJson().toJson(td)); + System.out.println(">> testInnerClassType: " + JsonUtils.toJson(td)); Assertions.assertEquals("org.apache.dubbo.metadata.definition.common.OuterClass.InnerClass", td.getType()); Assertions.assertEquals(1, td.getProperties().size()); Assertions.assertNotNull(td.getProperties().get("name")); ServiceDefinition sd = MetadataUtils.generateMetadata(TestService.class); - System.out.println(">> testInnerClassType: " + JsonUtils.getJson().toJson(sd)); + System.out.println(">> testInnerClassType: " + JsonUtils.toJson(sd)); Assertions.assertEquals(TestService.class.getName(), sd.getCanonicalName()); Assertions.assertEquals(TestService.class.getMethods().length, sd.getMethods().size()); @@ -76,7 +76,7 @@ class MetadataTest { void testRawMap() { TypeDefinitionBuilder builder = new TypeDefinitionBuilder(); TypeDefinition td = builder.build(ResultWithRawCollections.class, ResultWithRawCollections.class); - System.out.println(">> testRawMap: " + JsonUtils.getJson().toJson(td)); + System.out.println(">> testRawMap: " + JsonUtils.toJson(td)); Assertions.assertEquals("org.apache.dubbo.metadata.definition.common.ResultWithRawCollections", td.getType()); Assertions.assertEquals(2, td.getProperties().size()); @@ -84,7 +84,7 @@ class MetadataTest { Assertions.assertEquals("java.util.List", td.getProperties().get("list")); ServiceDefinition sd = MetadataUtils.generateMetadata(TestService.class); - System.out.println(">> testRawMap: " + JsonUtils.getJson().toJson(sd)); + System.out.println(">> testRawMap: " + JsonUtils.toJson(sd)); Assertions.assertEquals(TestService.class.getName(), sd.getCanonicalName()); Assertions.assertEquals(TestService.class.getMethods().length, sd.getMethods().size()); @@ -102,7 +102,7 @@ class MetadataTest { void testEnum() { TypeDefinitionBuilder builder = new TypeDefinitionBuilder(); TypeDefinition td = builder.build(ColorEnum.class, ColorEnum.class); - System.out.println(">> testEnum: " + JsonUtils.getJson().toJson(td)); + System.out.println(">> testEnum: " + JsonUtils.toJson(td)); Assertions.assertEquals("org.apache.dubbo.metadata.definition.common.ColorEnum", td.getType()); Assertions.assertEquals(3, td.getEnums().size()); @@ -111,7 +111,7 @@ class MetadataTest { Assertions.assertTrue(td.getEnums().contains("BLUE")); ServiceDefinition sd = MetadataUtils.generateMetadata(TestService.class); - System.out.println(">> testEnum: " + JsonUtils.getJson().toJson(sd)); + System.out.println(">> testEnum: " + JsonUtils.toJson(sd)); Assertions.assertEquals(TestService.class.getName(), sd.getCanonicalName()); Assertions.assertEquals(TestService.class.getMethods().length, sd.getMethods().size()); @@ -129,13 +129,13 @@ class MetadataTest { void testExtendsMap() { TypeDefinitionBuilder builder = new TypeDefinitionBuilder(); TypeDefinition td = builder.build(ClassExtendsMap.class, ClassExtendsMap.class); - System.out.println(">> testExtendsMap: " + JsonUtils.getJson().toJson(td)); + System.out.println(">> testExtendsMap: " + JsonUtils.toJson(td)); Assertions.assertEquals("org.apache.dubbo.metadata.definition.common.ClassExtendsMap", td.getType()); Assertions.assertEquals(0, td.getProperties().size()); ServiceDefinition sd = MetadataUtils.generateMetadata(TestService.class); - System.out.println(">> testExtendsMap: " + JsonUtils.getJson().toJson(sd)); + System.out.println(">> testExtendsMap: " + JsonUtils.toJson(sd)); Assertions.assertEquals(TestService.class.getName(), sd.getCanonicalName()); Assertions.assertEquals(TestService.class.getMethods().length, sd.getMethods().size()); @@ -148,4 +148,4 @@ class MetadataTest { } Assertions.assertFalse(containsType); } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/IsolationExecutorSupportFactoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/IsolationExecutorSupportFactoryTest.java new file mode 100644 index 0000000000..a0608064ed --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/IsolationExecutorSupportFactoryTest.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.executor; + +import org.apache.dubbo.common.URL; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class IsolationExecutorSupportFactoryTest { + @Test + void test() { + Assertions.assertInstanceOf(DefaultExecutorSupport.class, IsolationExecutorSupportFactory.getIsolationExecutorSupport(URL.valueOf("dubbo://"))); + Assertions.assertInstanceOf(DefaultExecutorSupport.class, IsolationExecutorSupportFactory.getIsolationExecutorSupport(URL.valueOf("empty://"))); + Assertions.assertInstanceOf(DefaultExecutorSupport.class, IsolationExecutorSupportFactory.getIsolationExecutorSupport(URL.valueOf("exchange://"))); + Assertions.assertInstanceOf(Mock1ExecutorSupport.class, IsolationExecutorSupportFactory.getIsolationExecutorSupport(URL.valueOf("mock1://"))); + Assertions.assertInstanceOf(Mock2ExecutorSupport.class, IsolationExecutorSupportFactory.getIsolationExecutorSupport(URL.valueOf("mock2://"))); + Assertions.assertInstanceOf(DefaultExecutorSupport.class, IsolationExecutorSupportFactory.getIsolationExecutorSupport(URL.valueOf("mock3://"))); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock1ExecutorSupport.java b/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock1ExecutorSupport.java new file mode 100644 index 0000000000..7e0182dce1 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock1ExecutorSupport.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.executor; + +import java.util.concurrent.Executor; + +public class Mock1ExecutorSupport implements ExecutorSupport { + @Override + public Executor getExecutor(Object data) { + return null; + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock1IsolationExecutorSupportFactory.java b/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock1IsolationExecutorSupportFactory.java new file mode 100644 index 0000000000..015fe5e4fb --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock1IsolationExecutorSupportFactory.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.executor; + +import org.apache.dubbo.common.URL; + +public class Mock1IsolationExecutorSupportFactory implements IsolationExecutorSupportFactory { + @Override + public ExecutorSupport createIsolationExecutorSupport(URL url) { + return new Mock1ExecutorSupport(); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock2ExecutorSupport.java b/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock2ExecutorSupport.java new file mode 100644 index 0000000000..93e130de60 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock2ExecutorSupport.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.executor; + +import java.util.concurrent.Executor; + +public class Mock2ExecutorSupport implements ExecutorSupport { + @Override + public Executor getExecutor(Object data) { + return null; + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock2IsolationExecutorSupportFactory.java b/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock2IsolationExecutorSupportFactory.java new file mode 100644 index 0000000000..f416813641 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock2IsolationExecutorSupportFactory.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.executor; + +import org.apache.dubbo.common.URL; + +public class Mock2IsolationExecutorSupportFactory implements IsolationExecutorSupportFactory { + @Override + public ExecutorSupport createIsolationExecutorSupport(URL url) { + return new Mock2ExecutorSupport(); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/FrameworkServiceRepositoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/FrameworkServiceRepositoryTest.java index 96909e6529..408fbc13c5 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/FrameworkServiceRepositoryTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/FrameworkServiceRepositoryTest.java @@ -16,7 +16,6 @@ */ package org.apache.dubbo.rpc.model; -import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.support.DemoService; @@ -82,12 +81,6 @@ class FrameworkServiceRepositoryTest { Assertions.assertEquals(providerModels.size(), 1); Assertions.assertEquals(providerModels.get(0), providerModel); - URL url = URL.valueOf("test://127.0.0.1:9103/" + DemoService.class.getName() + "?group=GROUP&version=1.0.0"); - frameworkServiceRepository.registerProviderUrl(url); - List urls = frameworkServiceRepository.lookupRegisteredProviderUrlsWithoutGroup(keyWithoutGroup(url.getServiceKey())); - Assertions.assertEquals(urls.size(), 1); - Assertions.assertEquals(urls.get(0), url); - ConsumerModel consumerModel = new ConsumerModel( serviceMetadata.getServiceKey(), new DemoServiceImpl(), serviceDescriptor, moduleModel, serviceMetadata, null, ClassUtils.getClassLoader(DemoService.class)); @@ -99,7 +92,6 @@ class FrameworkServiceRepositoryTest { frameworkServiceRepository.unregisterProvider(providerModel); Assertions.assertNull(frameworkServiceRepository.lookupExportedService(serviceKey)); Assertions.assertNull(frameworkServiceRepository.lookupExportedServiceWithoutGroup(keyWithoutGroup)); - Assertions.assertNull(frameworkServiceRepository.lookupRegisteredProviderUrlsWithoutGroup(keyWithoutGroup)); } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ModuleServiceRepositoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ModuleServiceRepositoryTest.java index 04d1f4eac2..b78e6cbe82 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ModuleServiceRepositoryTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ModuleServiceRepositoryTest.java @@ -62,7 +62,7 @@ class ModuleServiceRepositoryTest { Assertions.assertEquals(lookupServiceResult, serviceDescriptor); List allServices = repository.getAllServices(); - Assertions.assertEquals(allServices.size(), 1); + Assertions.assertEquals(1, allServices.size()); Assertions.assertEquals(allServices.get(0), serviceDescriptor); ServiceDescriptor serviceDescriptor1 = repository.registerService(DemoService.class.getSimpleName(), DemoService.class); @@ -76,14 +76,14 @@ class ModuleServiceRepositoryTest { repository.registerConsumer(consumerModel); List allReferredServices = repository.getReferredServices(); - Assertions.assertEquals(allReferredServices.size(), 1); + Assertions.assertEquals(1, allReferredServices.size()); Assertions.assertEquals(allReferredServices.get(0), consumerModel); List referredServices = repository.lookupReferredServices(DemoService.class.getName()); - Assertions.assertEquals(referredServices.size(), 1); + Assertions.assertEquals(1, referredServices.size()); Assertions.assertEquals(referredServices.get(0), consumerModel); - ConsumerModel referredService = repository.lookupReferredService(DemoService.class.getName()); + ConsumerModel referredService = repository.lookupReferredServices(DemoService.class.getName()).get(0); Assertions.assertEquals(referredService, consumerModel); // 3.test providerModel @@ -94,14 +94,14 @@ class ModuleServiceRepositoryTest { serviceMetadata, ClassUtils.getClassLoader(DemoService.class)); repository.registerProvider(providerModel); List allExportedServices = repository.getExportedServices(); - Assertions.assertEquals(allExportedServices.size(), 1); + Assertions.assertEquals(1, allExportedServices.size()); Assertions.assertEquals(allExportedServices.get(0), providerModel); ProviderModel exportedService = repository.lookupExportedService(DemoService.class.getName()); Assertions.assertEquals(exportedService, providerModel); List providerModels = frameworkModel.getServiceRepository().allProviderModels(); - Assertions.assertEquals(providerModels.size(), 1); + Assertions.assertEquals(1, providerModels.size()); Assertions.assertEquals(providerModels.get(0), providerModel); // 4.test destroy diff --git a/dubbo-common/src/test/java/org/apache/dubbo/rpc/service/GenericExceptionTest.java b/dubbo-common/src/test/java/org/apache/dubbo/rpc/service/GenericExceptionTest.java index faee7712d2..19e24c5d71 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/rpc/service/GenericExceptionTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/rpc/service/GenericExceptionTest.java @@ -30,8 +30,8 @@ class GenericExceptionTest { void jsonSupport() throws IOException { { GenericException src = new GenericException(); - String s = JsonUtils.getJson().toJson(src); - GenericException dst = JsonUtils.getJson().toJavaObject(s, GenericException.class); + String s = JsonUtils.toJson(src); + GenericException dst = JsonUtils.toJavaObject(s, GenericException.class); Assertions.assertEquals(src.getExceptionClass(), dst.getExceptionClass()); Assertions.assertEquals(src.getExceptionMessage(), dst.getExceptionMessage()); Assertions.assertEquals(src.getMessage(), dst.getMessage()); @@ -39,8 +39,8 @@ class GenericExceptionTest { } { GenericException src = new GenericException(this.getClass().getSimpleName(), "test"); - String s = JsonUtils.getJson().toJson(src); - GenericException dst = JsonUtils.getJson().toJavaObject(s, GenericException.class); + String s = JsonUtils.toJson(src); + GenericException dst = JsonUtils.toJavaObject(s, GenericException.class); Assertions.assertEquals(src.getExceptionClass(), dst.getExceptionClass()); Assertions.assertEquals(src.getExceptionMessage(), dst.getExceptionMessage()); Assertions.assertEquals(src.getMessage(), dst.getMessage()); @@ -49,8 +49,8 @@ class GenericExceptionTest { { Throwable throwable = new Throwable("throwable"); GenericException src = new GenericException(throwable); - String s = JsonUtils.getJson().toJson(src); - GenericException dst = JsonUtils.getJson().toJavaObject(s, GenericException.class); + String s = JsonUtils.toJson(src); + GenericException dst = JsonUtils.toJavaObject(s, GenericException.class); Assertions.assertEquals(src.getExceptionClass(), dst.getExceptionClass()); Assertions.assertEquals(src.getExceptionMessage(), dst.getExceptionMessage()); Assertions.assertEquals(src.getMessage(), dst.getMessage()); diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory new file mode 100644 index 0000000000..e743289f83 --- /dev/null +++ b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory @@ -0,0 +1,2 @@ +mock1=org.apache.dubbo.rpc.executor.Mock1IsolationExecutorSupportFactory +mock2=org.apache.dubbo.rpc.executor.Mock2IsolationExecutorSupportFactory diff --git a/dubbo-common/src/test/resources/META-INF/version b/dubbo-common/src/test/resources/META-INF/test-versions/dubbo-common similarity index 100% rename from dubbo-common/src/test/resources/META-INF/version rename to dubbo-common/src/test/resources/META-INF/test-versions/dubbo-common diff --git a/dubbo-common/src/test/resources/security/serialize.allowlist b/dubbo-common/src/test/resources/security/serialize.allowlist index 128005c845..10a691ce0b 100644 --- a/dubbo-common/src/test/resources/security/serialize.allowlist +++ b/dubbo-common/src/test/resources/security/serialize.allowlist @@ -19,3 +19,5 @@ com.example.DemoInterface com.sun.Interface1 + +org.apache.dubbo diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/generic/GenericServiceTest.java b/dubbo-compatible/src/test/java/org/apache/dubbo/generic/GenericServiceTest.java index 1deca6f2e3..a2848f9565 100644 --- a/dubbo-compatible/src/test/java/org/apache/dubbo/generic/GenericServiceTest.java +++ b/dubbo-compatible/src/test/java/org/apache/dubbo/generic/GenericServiceTest.java @@ -272,11 +272,11 @@ class GenericServiceTest { } Map bean2Map(ComplexObject complexObject) { - return JsonUtils.getJson().toJavaObject(JsonUtils.getJson().toJson(complexObject), Map.class); + return JsonUtils.toJavaObject(JsonUtils.toJson(complexObject), Map.class); } ComplexObject map2bean(Map map) { - return JsonUtils.getJson().toJavaObject(JsonUtils.getJson().toJson(map), ComplexObject.class); + return JsonUtils.toJavaObject(JsonUtils.toJson(map), ComplexObject.class); } ComplexObject createComplexObject(String var1, int var2, long l, String[] var3, List var4, ComplexObject.TestEnum testEnum) { diff --git a/dubbo-config/dubbo-config-api/pom.xml b/dubbo-config/dubbo-config-api/pom.xml index 4687ce2285..110b043ba1 100644 --- a/dubbo-config/dubbo-config-api/pom.xml +++ b/dubbo-config/dubbo-config-api/pom.xml @@ -223,7 +223,7 @@ org.testcontainers testcontainers - 1.17.6 + 1.18.0 test diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java index f628f803c8..61784867c3 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java @@ -36,18 +36,15 @@ import org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.config.utils.ConfigValidationUtils; import org.apache.dubbo.metadata.ServiceNameMapping; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; -import org.apache.dubbo.metrics.model.TimePair; +import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.registry.client.metadata.MetadataUtils; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProxyFactory; -import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.ServerService; import org.apache.dubbo.rpc.cluster.ConfiguratorFactory; -import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; @@ -427,34 +424,23 @@ public class ServiceConfig extends ServiceConfigBase { List registryURLs = ConfigValidationUtils.loadRegistries(this, true); - TimePair timePair = TimePair.start(); - ApplicationModel applicationModel = module.getApplicationModel(); - try { - applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class); - } catch (Throwable t) { - applicationModel = ApplicationModel.defaultModel(); - } - GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class); - int size = protocols.size() * registryURLs.size(); - eventMulticaster.publishEvent(new RegistryEvent.MetricsServiceRegisterEvent(applicationModel, timePair, getUniqueServiceName(), size)); + MetricsEventBus.post(RegistryEvent.toRsEvent(module.getApplicationModel(), getUniqueServiceName(), protocols.size() * registryURLs.size()), + () -> { + for (ProtocolConfig protocolConfig : protocols) { + String pathKey = URL.buildKey(getContextPath(protocolConfig) + .map(p -> p + "/" + path) + .orElse(path), group, version); + // stub service will use generated service name + if (!serverService) { + // In case user specified path, register service one more time to map it to path. + repository.registerService(pathKey, interfaceClass); + } + doExportUrlsFor1Protocol(protocolConfig, registryURLs); + } + return null; + } + ); - for (ProtocolConfig protocolConfig : protocols) { - String pathKey = URL.buildKey(getContextPath(protocolConfig) - .map(p -> p + "/" + path) - .orElse(path), group, version); - // stub service will use generated service name - if (!serverService) { - // In case user specified path, register service one more time to map it to path. - repository.registerService(pathKey, interfaceClass); - } - try { - doExportUrlsFor1Protocol(protocolConfig, registryURLs); - } catch (RpcException e) { - eventMulticaster.publishErrorEvent(new RegistryEvent.MetricsServiceRegisterEvent(applicationModel, timePair, getUniqueServiceName(), registryURLs.size())); - throw e; - } - } - eventMulticaster.publishFinishEvent(new RegistryEvent.MetricsServiceRegisterEvent(applicationModel, timePair, getUniqueServiceName(), size)); providerModel.setServiceUrls(urls); } @@ -485,6 +471,7 @@ public class ServiceConfig extends ServiceConfigBase { * Because executor is not a string type, it cannot be attached to the url parameter, so it is added to URL#attributes * and obtained it in IsolationExecutorRepository#createExecutor method */ + providerModel.getServiceMetadata().addAttribute(SERVICE_EXECUTOR, getExecutor()); url.getAttributes().put(SERVICE_EXECUTOR, getExecutor()); } } diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java index b3b9d5e630..2002ec0cb0 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java @@ -39,6 +39,7 @@ import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.SslConfig; +import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.bootstrap.builders.ApplicationBuilder; import org.apache.dubbo.config.bootstrap.builders.ConfigCenterBuilder; import org.apache.dubbo.config.bootstrap.builders.ConsumerBuilder; @@ -703,6 +704,12 @@ public final class DubboBootstrap { return this; } + public DubboBootstrap tracing(TracingConfig tracing){ + tracing.setScopeModel(applicationModel); + configManager.setTracing(tracing); + return this; + } + public DubboBootstrap ssl(SslConfig sslConfig) { sslConfig.setScopeModel(applicationModel); configManager.setSsl(sslConfig); diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/InternalServiceConfigBuilder.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/InternalServiceConfigBuilder.java index a0b27cdbf8..6e37707f28 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/InternalServiceConfigBuilder.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/InternalServiceConfigBuilder.java @@ -34,20 +34,17 @@ import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Collection; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_PROTOCOL_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.CORE_THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL; -import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY; @@ -61,6 +58,7 @@ public class InternalServiceConfigBuilder { private Integer port; private String registryId; private Class interfaceClass; + private Executor executor; private T ref; private InternalServiceConfigBuilder(ApplicationModel applicationModel) { @@ -78,6 +76,11 @@ public class InternalServiceConfigBuilder { return getThis(); } + public InternalServiceConfigBuilder executor(Executor executor) { + this.executor = executor; + return getThis(); + } + public InternalServiceConfigBuilder ref(T ref) { this.ref = ref; return getThis(); @@ -87,7 +90,7 @@ public class InternalServiceConfigBuilder { this.registryId = registryId; return getThis(); } - + public InternalServiceConfigBuilder protocol(String protocol, String key) { if (StringUtils.isEmpty(protocol) && StringUtils.isNotBlank(key)) { Map params = getApplicationConfig().getParameters(); @@ -273,13 +276,7 @@ public class InternalServiceConfigBuilder { serviceConfig.setGroup(applicationConfig.getName()); serviceConfig.setVersion("1.0.0"); - serviceConfig.setExecutes(100); // max tasks running at the same time - Map params = new HashMap<>(); - params.put(THREADPOOL_KEY, "cached"); - params.put(THREADS_KEY, "100"); - params.put(CORE_THREADS_KEY, "2"); - - serviceConfig.setParameters(params); + serviceConfig.setExecutor(executor); if (null != configConsumer) { configConsumer.accept(serviceConfig); diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java index 12d29ff2e5..bd30eb9b56 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java @@ -50,9 +50,9 @@ import org.apache.dubbo.config.utils.CompositeReferenceCache; import org.apache.dubbo.config.utils.ConfigValidationUtils; import org.apache.dubbo.metadata.report.MetadataReportFactory; import org.apache.dubbo.metadata.report.MetadataReportInstance; +import org.apache.dubbo.metrics.collector.ConfigCenterMetricsCollector; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; -import org.apache.dubbo.metrics.model.TimePair; +import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.metrics.report.MetricsReporter; import org.apache.dubbo.metrics.report.MetricsReporterFactory; @@ -201,6 +201,8 @@ public class DefaultApplicationDeployer extends AbstractDeployer configOptional = configManager.getMetrics(); + // TODO compatible with old usage of metrics, remove protocol check after new metrics is ready for use. - if (metricsConfig != null && PROTOCOL_PROMETHEUS.equals(metricsConfig.getProtocol())) { - collector.setCollectEnabled(true); - collector.collectApplication(applicationModel); - String protocol = metricsConfig.getProtocol(); - if (StringUtils.isNotEmpty(protocol)) { - MetricsReporterFactory metricsReporterFactory = getExtensionLoader(MetricsReporterFactory.class).getAdaptiveExtension(); - MetricsReporter metricsReporter = metricsReporterFactory.createMetricsReporter(metricsConfig.toUrl()); - metricsReporter.init(); - applicationModel.getBeanFactory().registerBean(metricsReporter); - } + boolean importMetricsPrometheus; // Use package references instead of config checks + try { + Class.forName("io.micrometer.prometheus.PrometheusConfig"); + importMetricsPrometheus = true; + } catch (ClassNotFoundException e) { + importMetricsPrometheus = false; } + + if (!importMetricsPrometheus) { + //use old metrics + return; + } + + MetricsConfig metricsConfig = configOptional.orElse(new MetricsConfig(applicationModel)); + if (StringUtils.isBlank(metricsConfig.getProtocol())) { + metricsConfig.setProtocol(PROTOCOL_PROMETHEUS); + } + collector.setCollectEnabled(true); + collector.collectApplication(applicationModel); + collector.setThreadpoolCollectEnabled(Optional.ofNullable(metricsConfig.getEnableThreadpool()).orElse(true)); + MetricsReporterFactory metricsReporterFactory = getExtensionLoader(MetricsReporterFactory.class).getAdaptiveExtension(); + MetricsReporter metricsReporter = metricsReporterFactory.createMetricsReporter(metricsConfig.toUrl()); + metricsReporter.init(); + applicationModel.getBeanFactory().registerBean(metricsReporter); } @@ -782,20 +798,33 @@ public class DefaultApplicationDeployer extends AbstractDeployer configMap = parseProperties(configContent); + Map appConfigMap = parseProperties(appConfigContent); + + environment.updateExternalConfigMap(configMap); + environment.updateAppExternalConfigMap(appConfigMap); + + // Add metrics + collector.increase4Initialized(configCenter.getConfigFile(), configCenter.getGroup(), + configCenter.getProtocol(), applicationModel.getApplicationName(), configMap.size()); + if (isNotEmpty(appGroup)) { + collector.increase4Initialized(appConfigFile, appGroup, + configCenter.getProtocol(), applicationModel.getApplicationName(), appConfigMap.size()); + } } catch (IOException e) { throw new IllegalStateException("Failed to parse configurations from Config Center.", e); } @@ -829,17 +858,18 @@ public class DefaultApplicationDeployer extends AbstractDeployer { + ServiceInstanceMetadataUtils.registerMetadataAndInstance(applicationModel); + return null; + } + ); } catch (Exception e) { - eventMulticaster.publishErrorEvent(new RegistryEvent.MetricsApplicationRegisterEvent(applicationModel, timePair)); logger.error(CONFIG_REGISTER_INSTANCE_ERROR, "configuration server disconnected", "", "Register instance error.", e); } + if (registered) { // scheduled task for updating Metadata and ServiceInstance asyncMetadataFuture = frameworkExecutorRepository.getSharedScheduledExecutor().scheduleWithFixedDelay(() -> { @@ -1054,6 +1084,16 @@ public class DefaultApplicationDeployer extends AbstractDeployer listener : listeners) { + try { + listener.onInitialize(applicationModel); + } catch (Throwable e) { + logger.error(CONFIG_FAILED_START_MODEL, "", "", getIdentifier() + " an exception occurred when handle initialize event", e); + } + } + } + private void exportMetadataService() { if (!isStarting()) { return; @@ -1107,11 +1147,12 @@ public class DefaultApplicationDeployer extends AbstractDeployer serviceConfig = InternalServiceConfigBuilder.newBuilder(applicationModel) .interfaceClass(MetricsService.class) .protocol(getMetricsConfig().getExportServiceProtocol()) .port(getMetricsConfig().getExportServicePort()) + .executor(internalServiceExecutor) .ref(metricsService) .registryId("internal-metrics-registry") .build(); diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultModuleDeployer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultModuleDeployer.java index 25b6363914..c611892073 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultModuleDeployer.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultModuleDeployer.java @@ -19,6 +19,7 @@ package org.apache.dubbo.config.deploy; import org.apache.dubbo.common.config.ReferenceCache; import org.apache.dubbo.common.deploy.AbstractDeployer; import org.apache.dubbo.common.deploy.ApplicationDeployer; +import org.apache.dubbo.common.deploy.DeployListener; import org.apache.dubbo.common.deploy.DeployState; import org.apache.dubbo.common.deploy.ModuleDeployListener; import org.apache.dubbo.common.deploy.ModuleDeployer; @@ -46,12 +47,12 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_START_MODEL; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNABLE_DESTROY_MODEL; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_REFERENCE_MODEL; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_EXPORT_SERVICE; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_WAIT_EXPORT_REFER; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_REFERENCE_MODEL; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_REFER_SERVICE; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_START_MODEL; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_WAIT_EXPORT_REFER; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNABLE_DESTROY_MODEL; /** * Export/refer services of module @@ -110,6 +111,8 @@ public class DefaultModuleDeployer extends AbstractDeployer impleme if (initialized) { return; } + onInitialize(); + loadConfigs(); // read ModuleConfig @@ -249,6 +252,16 @@ public class DefaultModuleDeployer extends AbstractDeployer impleme onModuleStopped(); } + private void onInitialize() { + for (DeployListener listener : listeners) { + try { + listener.onInitialize(moduleModel); + } catch (Throwable e) { + logger.error(CONFIG_FAILED_START_MODEL, "", "", getIdentifier() + " an exception occurred when handle initialize event", e); + } + } + } + private void onModuleStarting() { setStarting(); startFuture = new CompletableFuture(); diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java index aceaaf057b..dc62bc803e 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java @@ -19,6 +19,7 @@ package org.apache.dubbo.config.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ArgumentConfig; import org.apache.dubbo.config.MethodConfig; @@ -30,6 +31,7 @@ import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.List; +import java.util.concurrent.ExecutorService; import static java.util.Collections.emptyList; import static org.apache.dubbo.common.constants.CommonConstants.METADATA_SERVICE_PORT_KEY; @@ -55,11 +57,14 @@ public class ConfigurableMetadataServiceExporter { public synchronized ConfigurableMetadataServiceExporter export() { if (serviceConfig == null || !isExported()) { + ExecutorService internalServiceExecutor = applicationModel.getFrameworkModel().getBeanFactory() + .getBean(FrameworkExecutorRepository.class).getInternalServiceExecutor(); this.serviceConfig = InternalServiceConfigBuilder.newBuilder(applicationModel) .interfaceClass(MetadataService.class) .protocol(getApplicationConfig().getMetadataServiceProtocol(), METADATA_SERVICE_PROTOCOL_KEY) .port(getApplicationConfig().getMetadataServicePort(), METADATA_SERVICE_PORT_KEY) .registryId("internal-metadata-registry") + .executor(internalServiceExecutor) .ref(metadataService) .build(configConsumer -> configConsumer.setMethods(generateMethodConfig())); diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ExporterDeployListener.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ExporterDeployListener.java index bff2e2920e..e7237c875d 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ExporterDeployListener.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ExporterDeployListener.java @@ -28,6 +28,11 @@ import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_ public class ExporterDeployListener implements ApplicationDeployListener, Prioritized { protected volatile ConfigurableMetadataServiceExporter metadataServiceExporter; + @Override + public void onInitialize(ApplicationModel scopeModel) { + + } + @Override public void onStarting(ApplicationModel scopeModel) { diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java index 78c2b0d9dd..ef5469b64a 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java @@ -48,6 +48,7 @@ import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.SslConfig; +import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.monitor.MonitorFactory; import org.apache.dubbo.monitor.MonitorService; import org.apache.dubbo.registry.RegistryService; @@ -544,6 +545,12 @@ public class ConfigValidationUtils { } } + public static void validateTracingConfig(TracingConfig tracingConfig) { + if (tracingConfig == null) { + return; + } + } + public static void validateSslConfig(SslConfig sslConfig) { if (sslConfig == null) { return; diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/DefaultConfigValidator.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/DefaultConfigValidator.java index 69fdafa944..396f6afc49 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/DefaultConfigValidator.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/DefaultConfigValidator.java @@ -27,6 +27,7 @@ import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.SslConfig; +import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.context.ConfigValidator; public class DefaultConfigValidator implements ConfigValidator { @@ -51,6 +52,8 @@ public class DefaultConfigValidator implements ConfigValidator { ConfigValidationUtils.validateModuleConfig((ModuleConfig) config); } else if (config instanceof MetricsConfig) { ConfigValidationUtils.validateMetricsConfig((MetricsConfig) config); + } else if (config instanceof TracingConfig) { + ConfigValidationUtils.validateTracingConfig((TracingConfig) config); } else if (config instanceof SslConfig) { ConfigValidationUtils.validateSslConfig((SslConfig) config); } diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ApplicationConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ApplicationConfigTest.java index ee7be3f03c..a6ec4dac6e 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ApplicationConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ApplicationConfigTest.java @@ -18,7 +18,6 @@ package org.apache.dubbo.config; import org.apache.dubbo.config.bootstrap.DubboBootstrap; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +30,7 @@ import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; import static org.apache.dubbo.common.constants.CommonConstants.DUMP_DIRECTORY; -import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_ISOLATION; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP; import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE; import static org.hamcrest.MatcherAssert.assertThat; @@ -346,7 +345,7 @@ class ApplicationConfigTest { ApplicationConfig applicationConfig = DubboBootstrap.getInstance().getApplication(); Assertions.assertEquals(DUBBO, applicationConfig.getProtocol()); - Assertions.assertEquals(EXECUTOR_MANAGEMENT_MODE_DEFAULT, applicationConfig.getExecutorManagementMode()); + Assertions.assertEquals(EXECUTOR_MANAGEMENT_MODE_ISOLATION, applicationConfig.getExecutorManagementMode()); Assertions.assertEquals(Boolean.TRUE, applicationConfig.getEnableFileCache()); DubboBootstrap.getInstance().destroy(); diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ConsumerConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ConsumerConfigTest.java index 336f344165..7f2283e47d 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ConsumerConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ConsumerConfigTest.java @@ -274,7 +274,7 @@ class ConsumerConfigTest { consumerConfig.setConfigCenter(new ConfigCenterConfig()); try { - JsonUtils.getJson().toJson(consumerConfig); + JsonUtils.toJson(consumerConfig); } catch (Throwable t) { Assertions.fail(t); } diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MethodConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MethodConfigTest.java index 7b137c5762..6588e58d58 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MethodConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MethodConfigTest.java @@ -476,4 +476,4 @@ class MethodConfigTest { Map metaData = methodConfig.getMetaData(); Assertions.assertEquals(0, metaData.size(), "Expect empty metadata but found: "+metaData); } -} \ No newline at end of file +} diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MetricsConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MetricsConfigTest.java index f752b7e86c..13a29b8966 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MetricsConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MetricsConfigTest.java @@ -19,7 +19,7 @@ package org.apache.dubbo.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.config.nested.PrometheusConfig; - +import org.apache.dubbo.config.nested.HistogramConfig; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; @@ -47,6 +47,10 @@ class MetricsConfigTest { aggregation.setEnabled(true); metrics.setAggregation(aggregation); + HistogramConfig histogram = new HistogramConfig(); + histogram.setEnabled(true); + metrics.setHistogram(histogram); + URL url = metrics.toUrl(); assertThat(url.getProtocol(), equalTo(PROTOCOL_PROMETHEUS)); @@ -56,6 +60,7 @@ class MetricsConfigTest { assertThat(url.getParameter("prometheus.exporter.enabled"), equalTo("true")); assertThat(url.getParameter("prometheus.pushgateway.enabled"), equalTo("true")); assertThat(url.getParameter("aggregation.enabled"), equalTo("true")); + assertThat(url.getParameter("histogram.enabled"), equalTo("true")); } @Test @@ -117,4 +122,4 @@ class MetricsConfigTest { assertThat(metrics.getAggregation().getBucketNum(), equalTo(5)); assertThat(metrics.getAggregation().getTimeWindowSeconds(), equalTo(120)); } -} \ No newline at end of file +} diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java index cfbc745a3b..ca1e95d61f 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java @@ -435,7 +435,7 @@ class ReferenceConfigTest { Invoker invoker = originalInvoker.getInvoker(); Assertions.assertTrue(invoker instanceof InjvmInvoker); } - URL url = withCount.getUrl(); + URL url = withFilter.getUrl(); Assertions.assertEquals("application1", url.getParameter("application")); Assertions.assertEquals("value1", url.getParameter("key1")); Assertions.assertEquals("value2", url.getParameter("key2")); diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/MultiInstanceTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/MultiInstanceTest.java index 276afc4839..5987b8acf9 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/MultiInstanceTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/MultiInstanceTest.java @@ -896,6 +896,11 @@ class MultiInstanceTest { this.moduleModel = moduleModel; } + @Override + public void onInitialize(ModuleModel scopeModel) { + Assertions.assertEquals(moduleModel, scopeModel); + } + @Override public void onStarting(ModuleModel scopeModel) { Assertions.assertEquals(moduleModel, scopeModel); diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/MetadataServiceURLParamsMetadataCustomizerTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/MetadataServiceURLParamsMetadataCustomizerTest.java index 67f2122783..f8361128d5 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/MetadataServiceURLParamsMetadataCustomizerTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/MetadataServiceURLParamsMetadataCustomizerTest.java @@ -95,7 +95,7 @@ class MetadataServiceURLParamsMetadataCustomizerTest { String val = instance.getMetadata().get(METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME); Assertions.assertNotNull(val); - Map map = JsonUtils.getJson().toJavaObject(val, Map.class); + Map map = JsonUtils.toJavaObject(val, Map.class); Assertions.assertEquals(map.get(PORT_KEY), String.valueOf(metadataServiceURL.getPort())); Assertions.assertEquals(map.get(PROTOCOL_KEY), metadataServiceURL.getProtocol()); Assertions.assertEquals(map.get(VERSION_KEY), metadataServiceURL.getVersion()); diff --git a/dubbo-config/dubbo-config-spring/pom.xml b/dubbo-config/dubbo-config-spring/pom.xml index e7264d158d..42247d25a5 100644 --- a/dubbo-config/dubbo-config-spring/pom.xml +++ b/dubbo-config/dubbo-config-spring/pom.xml @@ -27,7 +27,7 @@ The spring config module of dubbo project false - 2.7.9 + 2.7.10 @@ -73,7 +73,7 @@ org.aspectj aspectjweaver - 1.9.5 + 1.9.19 test diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboConfigBeanInitializer.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboConfigBeanInitializer.java index 86ad34ccba..bf696b69fe 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboConfigBeanInitializer.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboConfigBeanInitializer.java @@ -29,6 +29,7 @@ import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.SslConfig; +import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.context.AbstractConfigManager; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.spring.ConfigCenterBean; @@ -109,6 +110,7 @@ public class DubboConfigBeanInitializer implements BeanFactoryAware, Initializin loadConfigBeansOfType(ConfigCenterBean.class, configManager); loadConfigBeansOfType(MetadataReportConfig.class, configManager); loadConfigBeansOfType(MetricsConfig.class, configManager); + loadConfigBeansOfType(TracingConfig.class, configManager); loadConfigBeansOfType(SslConfig.class, configManager); // load module config beans diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfiguration.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfiguration.java index c4f2ed87e2..e75bcf5cc7 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfiguration.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfiguration.java @@ -27,6 +27,7 @@ import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.SslConfig; +import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.spring.ConfigCenterBean; import com.alibaba.spring.beans.factory.annotation.EnableConfigurationBeanBinding; @@ -66,6 +67,7 @@ public class DubboConfigConfiguration { @EnableConfigurationBeanBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class), @EnableConfigurationBeanBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class), @EnableConfigurationBeanBinding(prefix = "dubbo.metrics", type = MetricsConfig.class), + @EnableConfigurationBeanBinding(prefix = "dubbo.tracing", type = TracingConfig.class), @EnableConfigurationBeanBinding(prefix = "dubbo.ssl", type = SslConfig.class) }) public static class Single { @@ -85,7 +87,8 @@ public class DubboConfigConfiguration { @EnableConfigurationBeanBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true), @EnableConfigurationBeanBinding(prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true), @EnableConfigurationBeanBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true), - @EnableConfigurationBeanBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true) + @EnableConfigurationBeanBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true), + @EnableConfigurationBeanBinding(prefix = "dubbo.tracing", type = TracingConfig.class) }) public static class Multiple { diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java index 832c91f0c4..8f63565b21 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java @@ -33,6 +33,7 @@ import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.config.nested.PrometheusConfig; +import org.apache.dubbo.config.nested.HistogramConfig; import org.apache.dubbo.config.spring.Constants; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.ServiceBean; @@ -265,6 +266,10 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser { AggregationConfig aggregation = new AggregationConfig(); assignProperties(aggregation, child, parserContext); beanDefinition.getPropertyValues().addPropertyValue("aggregation", aggregation); + }else if("histogram".equals(child.getNodeName()) || "histogram".equals(child.getLocalName())){ + HistogramConfig histogram = new HistogramConfig(); + assignProperties(histogram, child, parserContext); + beanDefinition.getPropertyValues().addPropertyValue("histogram", histogram); } else if ("prometheus-exporter".equals(child.getNodeName()) || "prometheus-exporter".equals(child.getLocalName())) { if (prometheus == null) { prometheus = new PrometheusConfig(); diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandler.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandler.java index 982b85dab7..1cf9bec517 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandler.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandler.java @@ -16,7 +16,6 @@ */ package org.apache.dubbo.config.spring.schema; -import org.apache.dubbo.common.Version; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MetadataReportConfig; @@ -27,6 +26,7 @@ import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.SslConfig; +import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.spring.ConfigCenterBean; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.ServiceBean; @@ -46,11 +46,6 @@ import org.w3c.dom.Element; * @export */ public class DubboNamespaceHandler extends NamespaceHandlerSupport implements ConfigurableSourceBeanMetadataElement { - - static { - Version.checkDuplicate(DubboNamespaceHandler.class); - } - @Override public void init() { registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class)); @@ -60,6 +55,7 @@ public class DubboNamespaceHandler extends NamespaceHandlerSupport implements Co registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class)); registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class)); + registerBeanDefinitionParser("tracing", new DubboBeanDefinitionParser(TracingConfig.class)); registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class)); diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd index d801ffefc3..ca5a6ec455 100644 --- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd +++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd @@ -356,7 +356,8 @@ - + + @@ -504,12 +505,14 @@ - + + - + + @@ -534,12 +537,14 @@ - + + - + + @@ -1044,6 +1049,7 @@ + @@ -1051,19 +1057,25 @@ - + - + + + + + + + - + @@ -1076,6 +1088,19 @@ + + + + + + + + + + + + + @@ -1155,6 +1180,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1457,7 +1540,8 @@ - + + @@ -2028,6 +2112,17 @@ + + + + + + + + + + + @@ -2045,4 +2140,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/YamlPropertySourceFactory.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/YamlPropertySourceFactory.java index 2b18644a36..225bcd1463 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/YamlPropertySourceFactory.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/YamlPropertySourceFactory.java @@ -22,6 +22,7 @@ import org.springframework.core.env.PropertySource; import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.PropertySourceFactory; import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.nodes.MappingNode; @@ -53,7 +54,7 @@ public class YamlPropertySourceFactory extends YamlProcessor implements Property @Override protected Yaml createYaml() { - return new Yaml(new Constructor() { + return new Yaml(new Constructor(new LoaderOptions()) { @Override protected Map constructMapping(MappingNode node) { try { @@ -82,7 +83,7 @@ public class YamlPropertySourceFactory extends YamlProcessor implements Property } }; } - }, new Representer(), + }, new Representer(new DumperOptions()), new DumperOptions(), new Resolver() { @Override public void addImplicitResolver(Tag tag, Pattern regexp, diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/configprops/SpringBootConfigPropsTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/configprops/SpringBootConfigPropsTest.java index 9ce1a7f5aa..5ef35dd5c2 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/configprops/SpringBootConfigPropsTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/configprops/SpringBootConfigPropsTest.java @@ -55,7 +55,7 @@ import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMET "dubbo.protocol.name=dubbo", "dubbo.protocol.port=20880", "dubbo.metrics.protocol=prometheus", - "dubbo.metrics.enable-jvm-metrics=true", + "dubbo.metrics.enable-jvm=true", "dubbo.metrics.prometheus.exporter.enabled=true", "dubbo.metrics.prometheus.exporter.enable-http-service-discovery=true", "dubbo.metrics.prometheus.exporter.http-service-discovery-url=localhost:8080", @@ -64,6 +64,7 @@ import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMET "dubbo.metrics.aggregation.enabled=true", "dubbo.metrics.aggregation.bucket-num=5", "dubbo.metrics.aggregation.time-window-seconds=120", + "dubbo.metrics.histogram.enabled=true", "dubbo.monitor.address=zookeeper://127.0.0.1:32770", "dubbo.Config-center.address=${zookeeper.connection.address.1}", "dubbo.config-Center.group=group1", @@ -116,6 +117,7 @@ class SpringBootConfigPropsTest { Assertions.assertEquals(5, metricsConfig.getAggregation().getBucketNum()); Assertions.assertEquals(120, metricsConfig.getAggregation().getTimeWindowSeconds()); Assertions.assertTrue(metricsConfig.getAggregation().getEnabled()); + Assertions.assertTrue(metricsConfig.getHistogram().getEnabled()); List defaultProtocols = configManager.getDefaultProtocols(); Assertions.assertEquals(1, defaultProtocols.size()); @@ -153,4 +155,4 @@ class SpringBootConfigPropsTest { } -} \ No newline at end of file +} diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/configprops/SpringBootMultipleConfigPropsTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/configprops/SpringBootMultipleConfigPropsTest.java index 9bb31c119c..482cd054cd 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/configprops/SpringBootMultipleConfigPropsTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/configprops/SpringBootMultipleConfigPropsTest.java @@ -63,6 +63,7 @@ import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMET "dubbo.metricses.my-metrics.aggregation.enabled=true", "dubbo.metricses.my-metrics.aggregation.bucket-num=5", "dubbo.metricses.my-metrics.aggregation.time-window-seconds=120", + "dubbo.metricses.my-metrics.histogram.enabled=true", "dubbo.monitors.my-monitor.address=zookeeper://127.0.0.1:32770", "dubbo.config-centers.my-configcenter.address=${zookeeper.connection.address.1}", "dubbo.config-centers.my-configcenter.group=group1", @@ -116,6 +117,7 @@ class SpringBootMultipleConfigPropsTest { Assertions.assertEquals(5, metricsConfig.getAggregation().getBucketNum()); Assertions.assertEquals(120, metricsConfig.getAggregation().getTimeWindowSeconds()); Assertions.assertTrue(metricsConfig.getAggregation().getEnabled()); + Assertions.assertTrue(metricsConfig.getHistogram().getEnabled()); List defaultProtocols = configManager.getDefaultProtocols(); Assertions.assertEquals(1, defaultProtocols.size()); @@ -154,4 +156,4 @@ class SpringBootMultipleConfigPropsTest { } -} \ No newline at end of file +} diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/metrics/SpringBootConfigMetricsTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/metrics/SpringBootConfigMetricsTest.java index 3b54f09bd0..c09cebe1ed 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/metrics/SpringBootConfigMetricsTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/metrics/SpringBootConfigMetricsTest.java @@ -16,8 +16,6 @@ */ package org.apache.dubbo.config.spring.metrics; -import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; - import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ConfigManager; @@ -31,6 +29,8 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; + @SpringBootTest( properties = { "dubbo.application.NAME = dubbo-demo-application", @@ -41,7 +41,7 @@ import org.springframework.context.annotation.Configuration; "dubbo.metrics.protocol=prometheus", "dubbo.metrics.export-service-protocol=tri", "dubbo.metrics.export-service-port=9999", - "dubbo.metrics.enable-jvm-metrics=true", + "dubbo.metrics.enable-jvm=true", "dubbo.metrics.prometheus.exporter.enabled=true", "dubbo.metrics.prometheus.exporter.enable-http-service-discovery=true", "dubbo.metrics.prometheus.exporter.http-service-discovery-url=localhost:8080", @@ -50,6 +50,7 @@ import org.springframework.context.annotation.Configuration; "dubbo.metrics.aggregation.enabled=true", "dubbo.metrics.aggregation.bucket-num=5", "dubbo.metrics.aggregation.time-window-seconds=120", + "dubbo.metrics.histogram.enabled=true", "dubbo.metadata-report.address=${zookeeper.connection.address.2}" }, classes = { @@ -79,7 +80,7 @@ public class SpringBootConfigMetricsTest { MetricsConfig metricsConfig = configManager.getMetrics().get(); Assertions.assertEquals(PROTOCOL_PROMETHEUS, metricsConfig.getProtocol()); - Assertions.assertTrue(metricsConfig.getEnableJvmMetrics()); + Assertions.assertTrue(metricsConfig.getEnableJvm()); Assertions.assertEquals("tri",metricsConfig.getExportServiceProtocol()); Assertions.assertEquals(9999, metricsConfig.getExportServicePort()); Assertions.assertTrue(metricsConfig.getPrometheus().getExporter().getEnabled()); @@ -90,6 +91,7 @@ public class SpringBootConfigMetricsTest { Assertions.assertEquals(5, metricsConfig.getAggregation().getBucketNum()); Assertions.assertEquals(120, metricsConfig.getAggregation().getTimeWindowSeconds()); Assertions.assertTrue(metricsConfig.getAggregation().getEnabled()); + Assertions.assertTrue(metricsConfig.getHistogram().getEnabled()); } } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandlerTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandlerTest.java index 2a5a16aef1..2109f15234 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandlerTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandlerTest.java @@ -256,7 +256,7 @@ class DubboNamespaceHandlerTest { MetricsConfig metricsBean = ctx.getBean(MetricsConfig.class); MetricsConfig metrics = configManager.getMetrics().get(); - assertTrue(metrics.getEnableJvmMetrics()); + assertTrue(metrics.getEnableJvm()); assertEquals(metrics.getAggregation().getEnabled(), true); assertEquals(metrics.getAggregation().getBucketNum(), 5); diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/metrics-aggregation.xml b/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/metrics-aggregation.xml index 96835f7703..c97ac1ad65 100644 --- a/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/metrics-aggregation.xml +++ b/dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/metrics-aggregation.xml @@ -24,7 +24,7 @@ - + diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/pom.xml b/dubbo-configcenter/dubbo-configcenter-apollo/pom.xml index d31dc785be..bb56439b4c 100644 --- a/dubbo-configcenter/dubbo-configcenter-apollo/pom.xml +++ b/dubbo-configcenter/dubbo-configcenter-apollo/pom.xml @@ -29,7 +29,7 @@ The Apollo implementation of the configcenter api false - 2.0.1 + 2.1.0 @@ -38,6 +38,21 @@ dubbo-common ${project.parent.version} + + org.apache.dubbo + dubbo-metrics-api + ${project.parent.version} + + + org.apache.dubbo + dubbo-metrics-default + ${project.parent.version} + + + org.apache.dubbo + dubbo-metrics-prometheus + ${project.parent.version} + com.ctrip.framework.apollo apollo-client diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java index 9525f7ce7e..1b33e85781 100644 --- a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java +++ b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java @@ -33,6 +33,8 @@ import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; import com.ctrip.framework.apollo.enums.ConfigSourceType; import com.ctrip.framework.apollo.enums.PropertyChangeType; import com.ctrip.framework.apollo.model.ConfigChange; +import org.apache.dubbo.metrics.collector.ConfigCenterMetricsCollector; +import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Arrays; import java.util.Collections; @@ -76,9 +78,11 @@ public class ApolloDynamicConfiguration implements DynamicConfiguration { private final Config dubboConfig; private final ConfigFile dubboConfigFile; private final ConcurrentMap listeners = new ConcurrentHashMap<>(); + private final ApplicationModel applicationModel; - ApolloDynamicConfiguration(URL url) { + ApolloDynamicConfiguration(URL url, ApplicationModel applicationModel) { this.url = url; + this.applicationModel = applicationModel; // Instead of using Dubbo's configuration, I would suggest use the original configuration method Apollo provides. String configEnv = url.getParameter(APOLLO_ENV_KEY); String configAddr = getAddressWithProtocolPrefix(url); @@ -245,6 +249,10 @@ public class ApolloDynamicConfiguration implements DynamicConfiguration { ConfigChangedEvent event = new ConfigChangedEvent(key, change.getNamespace(), change.getNewValue(), getChangeType(change)); listeners.forEach(listener -> listener.process(event)); + + ConfigCenterMetricsCollector collector = + applicationModel.getBeanFactory().getBean(ConfigCenterMetricsCollector.class); + collector.increaseUpdated("apollo", applicationModel.getApplicationName(), event); } } diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationFactory.java b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationFactory.java index 6a8ce304f4..64331d886b 100644 --- a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationFactory.java +++ b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationFactory.java @@ -19,13 +19,21 @@ package org.apache.dubbo.configcenter.support.apollo; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; +import org.apache.dubbo.rpc.model.ApplicationModel; /** * */ public class ApolloDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory { + + private ApplicationModel applicationModel; + + public ApolloDynamicConfigurationFactory(ApplicationModel applicationModel) { + this.applicationModel = applicationModel; + } + @Override protected DynamicConfiguration createDynamicConfiguration(URL url) { - return new ApolloDynamicConfiguration(url); + return new ApolloDynamicConfiguration(url, applicationModel); } } diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java b/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java index a54ffd37ad..07aa131e67 100644 --- a/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java +++ b/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java @@ -21,6 +21,7 @@ import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import com.google.common.util.concurrent.SettableFuture; +import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -46,6 +47,7 @@ class ApolloDynamicConfigurationTest { private static final String DEFAULT_NAMESPACE = "dubbo"; private static ApolloDynamicConfiguration apolloDynamicConfiguration; private static URL url; + private static ApplicationModel applicationModel; /** * The constant embeddedApollo. @@ -61,6 +63,7 @@ class ApolloDynamicConfigurationTest { String apolloUrl = System.getProperty("apollo.configService"); String urlForDubbo = "apollo://" + apolloUrl.substring(apolloUrl.lastIndexOf("/") + 1) + "/org.apache.dubbo.apollo.testService?namespace=dubbo&check=true"; url = URL.valueOf(urlForDubbo).addParameter(SESSION_TIMEOUT_KEY, 15000); + applicationModel = ApplicationModel.defaultModel(); } // /** @@ -88,7 +91,7 @@ class ApolloDynamicConfigurationTest { String mockKey = "mockKey1"; String mockValue = String.valueOf(new Random().nextInt()); putMockRuleData(mockKey, mockValue, DEFAULT_NAMESPACE); - apolloDynamicConfiguration = new ApolloDynamicConfiguration(url); + apolloDynamicConfiguration = new ApolloDynamicConfiguration(url, applicationModel); assertEquals(mockValue, apolloDynamicConfiguration.getConfig(mockKey, DEFAULT_NAMESPACE, 3000L)); mockKey = "notExistKey"; @@ -106,7 +109,7 @@ class ApolloDynamicConfigurationTest { String mockValue = String.valueOf(new Random().nextInt()); putMockRuleData(mockKey, mockValue, DEFAULT_NAMESPACE); TimeUnit.MILLISECONDS.sleep(1000); - apolloDynamicConfiguration = new ApolloDynamicConfiguration(url); + apolloDynamicConfiguration = new ApolloDynamicConfiguration(url, applicationModel); assertEquals(mockValue, apolloDynamicConfiguration.getInternalProperty(mockKey)); mockValue = "mockValue2"; @@ -129,7 +132,7 @@ class ApolloDynamicConfigurationTest { final SettableFuture future = SettableFuture.create(); - apolloDynamicConfiguration = new ApolloDynamicConfiguration(url); + apolloDynamicConfiguration = new ApolloDynamicConfiguration(url, applicationModel); apolloDynamicConfiguration.addListener(mockKey, DEFAULT_NAMESPACE, new ConfigurationListener() { @Override @@ -187,4 +190,4 @@ class ApolloDynamicConfigurationTest { } -} \ No newline at end of file +} diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/EmbeddedApolloJunit5.java b/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/EmbeddedApolloJunit5.java index 852ed2ebe9..1bb7f5527a 100644 --- a/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/EmbeddedApolloJunit5.java +++ b/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/EmbeddedApolloJunit5.java @@ -88,17 +88,17 @@ public class EmbeddedApolloJunit5 implements BeforeAllCallback, AfterAllCallback Map mergedConfigurations = mergeOverriddenProperties(namespace, configurations); apolloConfig.setConfigurations(mergedConfigurations); - return JsonUtils.getJson().toJson(apolloConfig); + return JsonUtils.toJson(apolloConfig); } private String mockLongPollBody(String notificationsStr) { - List oldNotifications = JsonUtils.getJson().toJavaList(notificationsStr, ApolloConfigNotification.class); + List oldNotifications = JsonUtils.toJavaList(notificationsStr, ApolloConfigNotification.class); List newNotifications = new ArrayList<>(); for (ApolloConfigNotification notification : oldNotifications) { newNotifications .add(new ApolloConfigNotification(notification.getNamespaceName(), notification.getNotificationId() + 1)); } - return JsonUtils.getJson().toJson(newNotifications); + return JsonUtils.toJson(newNotifications); } /** diff --git a/dubbo-configcenter/dubbo-configcenter-nacos/pom.xml b/dubbo-configcenter/dubbo-configcenter-nacos/pom.xml index 65364b8bed..a6bc25f483 100644 --- a/dubbo-configcenter/dubbo-configcenter-nacos/pom.xml +++ b/dubbo-configcenter/dubbo-configcenter-nacos/pom.xml @@ -41,5 +41,20 @@ com.alibaba.nacos nacos-client + + org.apache.dubbo + dubbo-metrics-api + ${project.parent.version} + + + org.apache.dubbo + dubbo-metrics-default + ${project.parent.version} + + + org.apache.dubbo + dubbo-metrics-prometheus + ${project.parent.version} + diff --git a/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java index 359d33770a..9a8e42b2bb 100644 --- a/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java +++ b/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java @@ -43,6 +43,8 @@ import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.AbstractSharedListener; import com.alibaba.nacos.api.exception.NacosException; +import org.apache.dubbo.metrics.collector.ConfigCenterMetricsCollector; +import org.apache.dubbo.rpc.model.ApplicationModel; import static com.alibaba.nacos.api.PropertyKeyConst.PASSWORD; import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR; @@ -80,6 +82,8 @@ public class NacosDynamicConfiguration implements DynamicConfiguration { */ private final NacosConfigServiceWrapper configService; + private ApplicationModel applicationModel; + /** * The map store the key to {@link NacosConfigListener} mapping */ @@ -87,10 +91,11 @@ public class NacosDynamicConfiguration implements DynamicConfiguration { private final MD5Utils md5Utils = new MD5Utils(); - NacosDynamicConfiguration(URL url) { + NacosDynamicConfiguration(URL url, ApplicationModel applicationModel) { this.nacosProperties = buildNacosProperties(url); this.configService = buildConfigService(url); - watchListenerMap = new ConcurrentHashMap<>(); + this.watchListenerMap = new ConcurrentHashMap<>(); + this.applicationModel = applicationModel; } private NacosConfigServiceWrapper buildConfigService(URL url) { @@ -339,6 +344,10 @@ public class NacosDynamicConfiguration implements DynamicConfiguration { cacheData.put(dataId, configInfo); } listeners.forEach(listener -> listener.process(event)); + + ConfigCenterMetricsCollector collector = + applicationModel.getBeanFactory().getOrRegisterBean(ConfigCenterMetricsCollector.class); + collector.increaseUpdated("nacos", applicationModel.getApplicationName(), event); } void addListener(ConfigurationListener configurationListener) { diff --git a/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationFactory.java b/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationFactory.java index 61c02b48cf..5d2196726d 100644 --- a/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationFactory.java +++ b/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationFactory.java @@ -23,12 +23,19 @@ import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.constants.CommonConstants; import com.alibaba.nacos.api.PropertyKeyConst; +import org.apache.dubbo.rpc.model.ApplicationModel; /** * The nacos implementation of {@link AbstractDynamicConfigurationFactory} */ public class NacosDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory { + private ApplicationModel applicationModel; + + public NacosDynamicConfigurationFactory(ApplicationModel applicationModel) { + this.applicationModel = applicationModel; + } + @Override protected DynamicConfiguration createDynamicConfiguration(URL url) { URL nacosURL = url; @@ -36,6 +43,6 @@ public class NacosDynamicConfigurationFactory extends AbstractDynamicConfigurati // Nacos use empty string as default name space, replace default namespace "dubbo" to "" nacosURL = url.removeParameter(PropertyKeyConst.NAMESPACE); } - return new NacosDynamicConfiguration(nacosURL); + return new NacosDynamicConfiguration(nacosURL, applicationModel); } } diff --git a/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationTest.java b/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationTest.java index 3f2bb525fd..831c74c833 100644 --- a/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationTest.java +++ b/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationTest.java @@ -17,14 +17,14 @@ package org.apache.dubbo.configcenter.support.nacos; +import com.alibaba.nacos.api.NacosFactory; +import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.exception.NacosException; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; - -import com.alibaba.nacos.api.NacosFactory; -import com.alibaba.nacos.api.config.ConfigService; -import com.alibaba.nacos.api.exception.NacosException; +import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -132,7 +132,7 @@ class NacosDynamicConfigurationTest { // timeout in 15 seconds. URL url = URL.valueOf(urlForDubbo) .addParameter(SESSION_TIMEOUT_KEY, 15000); - config = new NacosDynamicConfiguration(url); + config = new NacosDynamicConfiguration(url, ApplicationModel.defaultModel()); try { @@ -184,4 +184,4 @@ class NacosDynamicConfigurationTest { } } -} \ No newline at end of file +} diff --git a/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/RetryTest.java b/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/RetryTest.java index 3be9037380..32116ba247 100644 --- a/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/RetryTest.java +++ b/dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/RetryTest.java @@ -20,6 +20,7 @@ import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; @@ -34,6 +35,8 @@ import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; import static org.mockito.ArgumentMatchers.any; class RetryTest { + + private static ApplicationModel applicationModel = ApplicationModel.defaultModel(); @Test void testRetryCreate() { @@ -51,10 +54,10 @@ class RetryTest { URL url = URL.valueOf("nacos://127.0.0.1:8848") .addParameter("nacos.retry", 5) .addParameter("nacos.retry-wait", 10); - Assertions.assertThrows(IllegalStateException.class, () -> new NacosDynamicConfiguration(url)); + Assertions.assertThrows(IllegalStateException.class, () -> new NacosDynamicConfiguration(url, applicationModel)); try { - new NacosDynamicConfiguration(url); + new NacosDynamicConfiguration(url, applicationModel); } catch (Throwable t) { Assertions.fail(t); } @@ -78,7 +81,7 @@ class RetryTest { .addParameter("nacos.retry-wait", 10) .addParameter("nacos.check", "false"); try { - new NacosDynamicConfiguration(url); + new NacosDynamicConfiguration(url, applicationModel); } catch (Throwable t) { Assertions.fail(t); } @@ -110,10 +113,10 @@ class RetryTest { URL url = URL.valueOf("nacos://127.0.0.1:8848") .addParameter("nacos.retry", 5) .addParameter("nacos.retry-wait", 10); - Assertions.assertThrows(IllegalStateException.class, () -> new NacosDynamicConfiguration(url)); + Assertions.assertThrows(IllegalStateException.class, () -> new NacosDynamicConfiguration(url, applicationModel)); try { - new NacosDynamicConfiguration(url); + new NacosDynamicConfiguration(url, applicationModel); } catch (Throwable t) { Assertions.fail(t); } diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml b/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml index 8166ca072d..825fbbb289 100644 --- a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml +++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml @@ -60,6 +60,21 @@ org.apache.zookeeper zookeeper + + org.apache.dubbo + dubbo-metrics-api + ${project.parent.version} + + + org.apache.dubbo + dubbo-metrics-default + ${project.parent.version} + + + org.apache.dubbo + dubbo-metrics-prometheus + ${project.parent.version} + diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java index 7ae31afca0..df596926b7 100644 --- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java +++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java @@ -19,6 +19,7 @@ package org.apache.dubbo.configcenter.support.zookeeper; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; +import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -34,9 +35,9 @@ public class CacheListener { public CacheListener() { } - public ZookeeperDataListener addListener(String pathKey, ConfigurationListener configurationListener, String key, String group) { + public ZookeeperDataListener addListener(String pathKey, ConfigurationListener configurationListener, String key, String group, ApplicationModel applicationModel) { ZookeeperDataListener zookeeperDataListener = ConcurrentHashMapUtils.computeIfAbsent(pathKeyListeners, pathKey, - _pathKey -> new ZookeeperDataListener(_pathKey, key, group)); + _pathKey -> new ZookeeperDataListener(_pathKey, key, group, applicationModel)); zookeeperDataListener.addListener(configurationListener); return zookeeperDataListener; } diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDataListener.java b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDataListener.java index 22243d55ed..a21babe2a2 100644 --- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDataListener.java +++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDataListener.java @@ -20,8 +20,10 @@ import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.metrics.collector.ConfigCenterMetricsCollector; import org.apache.dubbo.remoting.zookeeper.DataListener; import org.apache.dubbo.remoting.zookeeper.EventType; +import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; @@ -30,16 +32,19 @@ import java.util.concurrent.CopyOnWriteArraySet; * one path has multi configurationListeners */ public class ZookeeperDataListener implements DataListener { - private final String path; - private final String key; - private final String group; - private final Set listeners; - public ZookeeperDataListener(String path, String key, String group) { + private String path; + private String key; + private String group; + private Set listeners; + private ApplicationModel applicationModel; + + public ZookeeperDataListener(String path, String key, String group, ApplicationModel applicationModel) { this.path = path; this.key = key; this.group = group; this.listeners = new CopyOnWriteArraySet<>(); + this.applicationModel = applicationModel; } public void addListener(ConfigurationListener configurationListener) { @@ -71,6 +76,10 @@ public class ZookeeperDataListener implements DataListener { if (CollectionUtils.isNotEmpty(listeners)) { listeners.forEach(listener -> listener.process(configChangeEvent)); } + + ConfigCenterMetricsCollector collector = + applicationModel.getBeanFactory().getBean(ConfigCenterMetricsCollector.class); + collector.increaseUpdated("zookeeper", applicationModel.getApplicationName(), configChangeEvent); } } diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java index 423c2834f6..e0bc325c4c 100644 --- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java +++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java @@ -26,6 +26,7 @@ import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.remoting.zookeeper.ZookeeperClient; import org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter; +import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.zookeeper.data.Stat; import java.util.Collection; @@ -47,11 +48,13 @@ public class ZookeeperDynamicConfiguration extends TreePathDynamicConfiguration private static final int DEFAULT_ZK_EXECUTOR_THREADS_NUM = 1; private static final int DEFAULT_QUEUE = 10000; private static final Long THREAD_KEEP_ALIVE_TIME = 0L; + private final ApplicationModel applicationModel; - ZookeeperDynamicConfiguration(URL url, ZookeeperTransporter zookeeperTransporter) { + ZookeeperDynamicConfiguration(URL url, ZookeeperTransporter zookeeperTransporter, ApplicationModel applicationModel) { super(url); this.cacheListener = new CacheListener(); + this.applicationModel = applicationModel; final String threadName = this.getClass().getSimpleName(); this.executor = new ThreadPoolExecutor(DEFAULT_ZK_EXECUTOR_THREADS_NUM, DEFAULT_ZK_EXECUTOR_THREADS_NUM, @@ -150,7 +153,7 @@ public class ZookeeperDynamicConfiguration extends TreePathDynamicConfiguration if (cachedListener != null) { cachedListener.addListener(listener); } else { - ZookeeperDataListener addedListener = cacheListener.addListener(pathKey, listener, key, group); + ZookeeperDataListener addedListener = cacheListener.addListener(pathKey, listener, key, group, applicationModel); zkClient.addDataListener(pathKey, addedListener, executor); } } diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationFactory.java b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationFactory.java index f470654ca5..1ab7497379 100644 --- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationFactory.java +++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationFactory.java @@ -26,12 +26,15 @@ public class ZookeeperDynamicConfigurationFactory extends AbstractDynamicConfigu private final ZookeeperTransporter zookeeperTransporter; + private final ApplicationModel applicationModel; + public ZookeeperDynamicConfigurationFactory(ApplicationModel applicationModel) { + this.applicationModel = applicationModel; this.zookeeperTransporter = ZookeeperTransporter.getExtension(applicationModel); } @Override protected DynamicConfiguration createDynamicConfiguration(URL url) { - return new ZookeeperDynamicConfiguration(url, zookeeperTransporter); + return new ZookeeperDynamicConfiguration(url, zookeeperTransporter, applicationModel); } } diff --git a/dubbo-demo/dubbo-demo-annotation/pom.xml b/dubbo-demo/dubbo-demo-annotation/pom.xml index b3991acd71..b84ceb28f7 100644 --- a/dubbo-demo/dubbo-demo-annotation/pom.xml +++ b/dubbo-demo/dubbo-demo-annotation/pom.xml @@ -30,7 +30,7 @@ true - 2.7.9 + 2.7.10 diff --git a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/pom.xml b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/pom.xml index 32de3399ac..5c672fa079 100644 --- a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/pom.xml +++ b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/pom.xml @@ -55,10 +55,6 @@ org.apache.dubbo dubbo-registry-nacos - - com.alibaba.nacos - nacos-client - org.apache.dubbo dubbo-configcenter-zookeeper diff --git a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java index b9f31c9db8..e655d4a637 100644 --- a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java +++ b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java @@ -17,26 +17,23 @@ package org.apache.dubbo.demo.consumer; import org.apache.dubbo.common.constants.CommonConstants; -import org.apache.dubbo.config.ApplicationConfig; -import org.apache.dubbo.config.MetadataReportConfig; + import org.apache.dubbo.config.ProtocolConfig; -import org.apache.dubbo.config.ReferenceConfig; +import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.demo.DemoService; import org.apache.dubbo.rpc.service.GenericService; public class Application { - public static void main(String[] args) { - if (isClassic(args)) { - runWithRefer(); - } else { - runWithBootstrap(); - } - } - private static boolean isClassic(String[] args) { - return args.length > 0 && "classic".equalsIgnoreCase(args[0]); + private static final String REGISTRY_URL = "zookeeper://127.0.0.1:2181"; + + + + public static void main(String[] args) { + runWithBootstrap(); } private static void runWithBootstrap() { @@ -46,7 +43,7 @@ public class Application { DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap.application(new ApplicationConfig("dubbo-demo-api-consumer")) - .registry(new RegistryConfig("zookeeper://127.0.0.1:2181")) + .registry(new RegistryConfig(REGISTRY_URL)) .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1)) .reference(reference) .start(); @@ -59,17 +56,7 @@ public class Application { GenericService genericService = (GenericService) demoService; Object genericInvokeResult = genericService.$invoke("sayHello", new String[]{String.class.getName()}, new Object[]{"dubbo generic invoke"}); - System.out.println(genericInvokeResult); + System.out.println(genericInvokeResult.toString()); } - private static void runWithRefer() { - ReferenceConfig reference = new ReferenceConfig<>(); - reference.setApplication(new ApplicationConfig("dubbo-demo-api-consumer")); - reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181")); - reference.setMetadataReportConfig(new MetadataReportConfig("zookeeper://127.0.0.1:2181")); - reference.setInterface(DemoService.class); - DemoService service = reference.get(); - String message = service.sayHello("dubbo"); - System.out.println(message); - } } diff --git a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/pom.xml b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/pom.xml index 959d94ca72..cedee67977 100644 --- a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/pom.xml +++ b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/pom.xml @@ -57,10 +57,6 @@ org.apache.dubbo dubbo-registry-nacos - - com.alibaba.nacos - nacos-client - org.apache.dubbo dubbo-configcenter-zookeeper @@ -94,18 +90,5 @@ dubbo-serialization-fastjson2 - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-log4j12 - ${slf4j-log4j12.version} - - - log4j - log4j - diff --git a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/Application.java b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/Application.java index 73a5d73cee..b80bfbfd3b 100644 --- a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/Application.java +++ b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/Application.java @@ -18,26 +18,18 @@ package org.apache.dubbo.demo.provider; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.config.ApplicationConfig; -import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.demo.DemoService; -import java.util.concurrent.CountDownLatch; - public class Application { - public static void main(String[] args) throws Exception { - if (isClassic(args)) { - startWithExport(); - } else { - startWithBootstrap(); - } - } - private static boolean isClassic(String[] args) { - return args.length > 0 && "classic".equalsIgnoreCase(args[0]); + private static final String REGISTRY_URL = "zookeeper://127.0.0.1:2181"; + + public static void main(String[] args) { + startWithBootstrap(); } private static void startWithBootstrap() { @@ -47,23 +39,11 @@ public class Application { DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap.application(new ApplicationConfig("dubbo-demo-api-provider")) - .registry(new RegistryConfig("zookeeper://127.0.0.1:2181")) + .registry(new RegistryConfig(REGISTRY_URL)) .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1)) .service(service) .start() .await(); } - private static void startWithExport() throws InterruptedException { - ServiceConfig service = new ServiceConfig<>(); - service.setInterface(DemoService.class); - service.setRef(new DemoServiceImpl()); - service.setApplication(new ApplicationConfig("dubbo-demo-api-provider")); - service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181")); - service.setMetadataReportConfig(new MetadataReportConfig("zookeeper://127.0.0.1:2181")); - service.export(); - - System.out.println("dubbo service started"); - new CountDownLatch(1).await(); - } } diff --git a/dubbo-demo/dubbo-demo-api/pom.xml b/dubbo-demo/dubbo-demo-api/pom.xml index a4957977fe..c68afe43a4 100644 --- a/dubbo-demo/dubbo-demo-api/pom.xml +++ b/dubbo-demo/dubbo-demo-api/pom.xml @@ -36,7 +36,7 @@ true - 2.7.9 + 2.7.10 dubbo-demo-api diff --git a/dubbo-demo/dubbo-demo-generic-call/src/main/java/org/apache/dubbo/demo/consumer/GenericApplication.java b/dubbo-demo/dubbo-demo-generic-call/src/main/java/org/apache/dubbo/demo/consumer/GenericApplication.java index b7845f92ac..7aceba49a0 100644 --- a/dubbo-demo/dubbo-demo-generic-call/src/main/java/org/apache/dubbo/demo/consumer/GenericApplication.java +++ b/dubbo-demo/dubbo-demo-generic-call/src/main/java/org/apache/dubbo/demo/consumer/GenericApplication.java @@ -41,7 +41,7 @@ public class GenericApplication { if (args.length > 0 && CommonConstants.GENERIC_SERIALIZATION_GSON.equals(args[0])) { reference.setGeneric(CommonConstants.GENERIC_SERIALIZATION_GSON); - param = JsonUtils.getJson().toJson(param + " gson"); + param = JsonUtils.toJson(param + " gson"); } else { reference.setGeneric("true"); } diff --git a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/pom.xml b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/pom.xml index 460655a982..9d8c06ba24 100644 --- a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/pom.xml +++ b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/pom.xml @@ -33,7 +33,6 @@ - org.apache.dubbo dubbo-demo-native-interface @@ -102,7 +101,7 @@ dubbo-maven-plugin ${project.version} - com.apache.dubbo.demo.graalvm.consumer.Application + org.apache.dubbo.demo.graalvm.consumer.Application @@ -141,7 +140,7 @@ true 22.3 - com.apache.dubbo.demo.graalvm.consumer.Application + org.apache.dubbo.demo.graalvm.consumer.Application diff --git a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/java/com/apache/dubbo/demo/graalvm/consumer/Application.java b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/java/org/apache/dubbo/demo/graalvm/consumer/Application.java similarity index 73% rename from dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/java/com/apache/dubbo/demo/graalvm/consumer/Application.java rename to dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/java/org/apache/dubbo/demo/graalvm/consumer/Application.java index ca7c5b0945..f430cfd7b9 100644 --- a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/java/com/apache/dubbo/demo/graalvm/consumer/Application.java +++ b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/java/org/apache/dubbo/demo/graalvm/consumer/Application.java @@ -14,35 +14,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.apache.dubbo.demo.graalvm.consumer; +package org.apache.dubbo.demo.graalvm.consumer; +import org.apache.dubbo.graalvm.demo.DemoService; import org.apache.dubbo.common.constants.CommonConstants; -import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ReferenceConfig; +import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; -import org.apace.dubbo.graalvm.demo.DemoService; import java.util.HashMap; import java.util.Map; public class Application { + private static final String REGISTRY_URL = "zookeeper://127.0.0.1:2181"; + + public static void main(String[] args) { System.setProperty("dubbo.application.logger", "log4j"); System.setProperty("native", "true"); System.setProperty("dubbo.json-framework.prefer", "fastjson"); - if (isClassic(args)) { - runWithRefer(); - } else { - runWithBootstrap(); - } - } - - private static boolean isClassic(String[] args) { - return args.length > 0 && "classic".equalsIgnoreCase(args[0]); + runWithBootstrap(); } private static void runWithBootstrap() { @@ -62,7 +57,7 @@ public class Application { ProtocolConfig protocolConfig = new ProtocolConfig(CommonConstants.DUBBO, -1); protocolConfig.setSerialization("fastjson2"); bootstrap.application(applicationConfig) - .registry(new RegistryConfig("zookeeper://127.0.0.1:2181")) + .registry(new RegistryConfig(REGISTRY_URL)) .protocol(protocolConfig) .reference(reference) .start(); @@ -72,13 +67,4 @@ public class Application { System.out.println(message); } - private static void runWithRefer() { - ReferenceConfig reference = new ReferenceConfig<>(); - reference.setApplication(new ApplicationConfig("dubbo-demo-api-consumer")); - reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181")); - reference.setInterface(DemoService.class); - DemoService service = reference.get(); - String message = service.sayHello("dubbo"); - System.out.println(message); - } } diff --git a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/resources/Log4j.properties b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/resources/Log4j.properties index 4e90fa24e7..dc3923d6e3 100644 --- a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/resources/Log4j.properties +++ b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/resources/Log4j.properties @@ -17,7 +17,6 @@ # # - log4j.rootLogger=error, console log4j.appender.console=org.apache.log4j.ConsoleAppender diff --git a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/resources/META-INF/native-image/proxy-config.json b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/resources/META-INF/native-image/proxy-config.json index 49f2fc2d51..cb73756e52 100644 --- a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/resources/META-INF/native-image/proxy-config.json +++ b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/src/main/resources/META-INF/native-image/proxy-config.json @@ -1,6 +1,6 @@ [ [ - "org.apace.dubbo.graalvm.demo.DemoService", + "org.apache.dubbo.graalvm.demo.DemoService", "org.apache.dubbo.rpc.service.EchoService", "org.apache.dubbo.rpc.service.Destroyable" ] diff --git a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-interface/src/main/java/org/apace/dubbo/graalvm/demo/DemoService.java b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-interface/src/main/java/org/apache/dubbo/graalvm/demo/DemoService.java similarity index 95% rename from dubbo-demo/dubbo-demo-native/dubbo-demo-native-interface/src/main/java/org/apace/dubbo/graalvm/demo/DemoService.java rename to dubbo-demo/dubbo-demo-native/dubbo-demo-native-interface/src/main/java/org/apache/dubbo/graalvm/demo/DemoService.java index 566d270092..06097a6c1f 100644 --- a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-interface/src/main/java/org/apace/dubbo/graalvm/demo/DemoService.java +++ b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-interface/src/main/java/org/apache/dubbo/graalvm/demo/DemoService.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apace.dubbo.graalvm.demo; +package org.apache.dubbo.graalvm.demo; public interface DemoService { diff --git a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/pom.xml b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/pom.xml index 776f2b3842..0e03083dbf 100644 --- a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/pom.xml +++ b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/pom.xml @@ -74,18 +74,14 @@ org.apache.dubbo dubbo-remoting-http - com.alibaba fastjson - org.apache.dubbo dubbo-native - - diff --git a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/src/main/java/org/apache/dubbo/demo/graalvm/provider/Application.java b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/src/main/java/org/apache/dubbo/demo/graalvm/provider/Application.java index 9d65b50090..f324635955 100644 --- a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/src/main/java/org/apache/dubbo/demo/graalvm/provider/Application.java +++ b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/src/main/java/org/apache/dubbo/demo/graalvm/provider/Application.java @@ -16,31 +16,28 @@ */ package org.apache.dubbo.demo.graalvm.provider; +import org.apache.dubbo.graalvm.demo.DemoService; import org.apache.dubbo.common.constants.CommonConstants; -import org.apache.dubbo.config.ApplicationConfig; -import org.apache.dubbo.config.ProtocolConfig; -import org.apache.dubbo.config.RegistryConfig; -import org.apache.dubbo.config.ServiceConfig; -import org.apache.dubbo.config.bootstrap.DubboBootstrap; -import org.apace.dubbo.graalvm.demo.DemoService; -import org.apache.dubbo.rpc.model.ModuleModel; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ServiceConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.bootstrap.DubboBootstrap; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.CountDownLatch; public class Application { + private static final String REGISTRY_URL = "zookeeper://127.0.0.1:2181"; + + public static void main(String[] args) throws Exception { System.setProperty("dubbo.application.logger", "log4j"); System.setProperty("native", "true"); System.setProperty("dubbo.json-framework.prefer", "fastjson"); - if (isClassic(args)) { - startWithExport(); - } else { - startWithBootstrap(); - } + startWithBootstrap(); System.in.read(); } @@ -65,7 +62,7 @@ public class Application { ProtocolConfig protocolConfig = new ProtocolConfig(CommonConstants.DUBBO, -1); protocolConfig.setSerialization("fastjson2"); bootstrap.application(applicationConfig) - .registry(new RegistryConfig("zookeeper://127.0.0.1:2181")) + .registry(new RegistryConfig(REGISTRY_URL)) .protocol(protocolConfig) .service(service) .start() @@ -74,27 +71,4 @@ public class Application { System.out.println("dubbo service started"); } - private static void startWithExport() throws InterruptedException { - ApplicationConfig applicationConfig = new ApplicationConfig("dubbo-demo-api-provider"); - applicationConfig.setQosEnable(false); - applicationConfig.setCompiler("jdk"); - - Map m = new HashMap<>(1); - m.put("proxy", "jdk"); - applicationConfig.setParameters(m); - - ModuleModel moduleModel = applicationConfig.getApplicationModel().newModule(); - - - ServiceConfig service = new ServiceConfig<>(moduleModel); - service.setInterface(DemoService.class); - service.setRef(new DemoServiceImpl()); - - service.setApplication(applicationConfig); - service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181")); - service.export(); - - System.out.println("dubbo service started"); - new CountDownLatch(1).await(); - } } diff --git a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/src/main/java/org/apache/dubbo/demo/graalvm/provider/DemoServiceImpl.java b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/src/main/java/org/apache/dubbo/demo/graalvm/provider/DemoServiceImpl.java index 38a0348dde..38c9d1b45f 100644 --- a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/src/main/java/org/apache/dubbo/demo/graalvm/provider/DemoServiceImpl.java +++ b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/src/main/java/org/apache/dubbo/demo/graalvm/provider/DemoServiceImpl.java @@ -18,7 +18,7 @@ package org.apache.dubbo.demo.graalvm.provider; import org.apache.dubbo.rpc.RpcContext; -import org.apace.dubbo.graalvm.demo.DemoService; +import org.apache.dubbo.graalvm.demo.DemoService; public class DemoServiceImpl implements DemoService { diff --git a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/src/main/resources/META-INF/native-image/reflect-config.json b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/src/main/resources/META-INF/native-image/reflect-config.json index 0c8a2c368c..a0cb4a8a36 100644 --- a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/src/main/resources/META-INF/native-image/reflect-config.json +++ b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/src/main/resources/META-INF/native-image/reflect-config.json @@ -1,6 +1,6 @@ [ { - "name": "org.apace.dubbo.graalvm.demo.DemoService", + "name": "org.apache.dubbo.graalvm.demo.DemoService", "allPublicMethods": true }, { diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-consumer/pom.xml b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-consumer/pom.xml index 90616e8f9d..36a26644aa 100644 --- a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-consumer/pom.xml +++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-consumer/pom.xml @@ -31,7 +31,7 @@ 8 8 1.7.33 - 2.7.9 + 2.7.10 true @@ -47,16 +47,6 @@ dubbo-spring-boot-starter - - org.apache.dubbo - dubbo-rpc-api - - - - org.apache.dubbo - dubbo-rpc-dubbo - - org.apache.dubbo dubbo-registry-zookeeper @@ -133,15 +123,14 @@ log4j - - io.micrometer - micrometer-tracing-bridge-otel + org.apache.dubbo + dubbo-spring-boot-observability-starter org.apache.dubbo - dubbo-spring-boot-observability-starter + dubbo-qos diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml index da3de4b5e7..6df0e80c2a 100644 --- a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml +++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml @@ -31,7 +31,7 @@ 8 8 1.7.33 - 2.7.9 + 2.7.10 true @@ -42,21 +42,6 @@ ${project.parent.version} - - org.apache.dubbo - dubbo-spring-boot-starter - - - - org.apache.dubbo - dubbo-rpc-api - - - - org.apache.dubbo - dubbo-rpc-dubbo - - org.apache.dubbo dubbo-registry-zookeeper @@ -133,15 +118,16 @@ - - io.micrometer - micrometer-tracing-bridge-otel - org.apache.dubbo dubbo-spring-boot-observability-starter + + org.apache.dubbo + dubbo-qos + + diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml index 19a08111b6..d06fdfb28c 100644 --- a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml +++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml @@ -31,7 +31,16 @@ dubbo: address: zookeeper://127.0.0.1:2181 metadata-report: address: zookeeper://127.0.0.1:2181 - + metrics: + protocol: prometheus + enable-jvm: true + enable-registry: true + aggregation: + enabled: true + prometheus: + exporter: + enabled: true + enable-metadata: true logging: pattern: level: '%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]' diff --git a/dubbo-demo/dubbo-demo-spring-boot/pom.xml b/dubbo-demo/dubbo-demo-spring-boot/pom.xml index 4ac5160226..69e1e07ed6 100644 --- a/dubbo-demo/dubbo-demo-spring-boot/pom.xml +++ b/dubbo-demo/dubbo-demo-spring-boot/pom.xml @@ -36,9 +36,9 @@ 8 8 true - 2.7.9 - 2.7.9 - 1.10.5 + 2.7.10 + 2.7.10 + 1.10.6 diff --git a/dubbo-demo/dubbo-demo-triple/pom.xml b/dubbo-demo/dubbo-demo-triple/pom.xml index 3f19af44ae..e336afc1a4 100644 --- a/dubbo-demo/dubbo-demo-triple/pom.xml +++ b/dubbo-demo/dubbo-demo-triple/pom.xml @@ -124,7 +124,7 @@ kr.motd.maven os-maven-plugin - 1.6.2 + 1.7.1 initialize diff --git a/dubbo-demo/dubbo-demo-xml/pom.xml b/dubbo-demo/dubbo-demo-xml/pom.xml index b5b32d519c..6e4c678b2e 100644 --- a/dubbo-demo/dubbo-demo-xml/pom.xml +++ b/dubbo-demo/dubbo-demo-xml/pom.xml @@ -32,7 +32,7 @@ true - 2.7.9 + 2.7.10 diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml index 6308a9e1a4..8ad618c5ee 100644 --- a/dubbo-dependencies-bom/pom.xml +++ b/dubbo-dependencies-bom/pom.xml @@ -94,15 +94,15 @@ 5.3.25 5.8.2 3.29.2-GA - 1.14.2 + 1.14.4 3.2.10.Final - 4.1.87.Final + 4.1.91.Final 2.2.1 2.4.4 4.5.14 4.4.16 1.2.83 - 2.0.23 + 2.0.27 3.4.14 4.3.0 2.12.0 @@ -117,31 +117,30 @@ 3.22.2 1.3.2 3.1.0 - 5.0.0 9.4.51.v20230217 - 3.0.1 + 3.0.2 1.1.0.Final 5.4.3.Final 7.0.5.Final 3.0.1-b12 - 1.0.0 + 1.1.1 4.0.2 0.45 2.57 1.11.1 2.1.0 - 1.32 + 1.33 3.12.0 1.8.0 0.1.35 - 1.10.5 + 1.10.6 1.0.3 3.3 0.16.0 1.0.4 3.5.4 - 3.14.2 + 3.14.9 2.1.1 3.15.6.Final @@ -149,7 +148,7 @@ 8.5.87 0.7.5 2.1.2 - 1.53.0 + 1.54.0 0.8.1 1.2.1 @@ -170,26 +169,26 @@ 6.4.1 - 1.0.8 + 1.0.11 2.2.7 1.2.0 - 1.17.6 + 1.18.0 0.7.5 3.2.13 - 1.5.24 + 1.6.10 - 1.1.8.4 + 1.1.9.1 1.70 - 2.0.1 + 2.0.6 5.4.3 - 2.8.9 - 2.14.0 + 2.10.1 + 2.14.2 1.6 6.1.26 2.0 - 1.1.0 - 1.22 + 1.4.1 + 1.23.0 3.3.0-beta.1-SNAPSHOT @@ -423,13 +422,6 @@ ${servlet_version} - - - jakarta.servlet - jakarta.servlet-api - ${jakarta.servlet_version} - - com.squareup.okhttp3 okhttp diff --git a/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml b/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml index 0ffa48b90a..d7e52b7e21 100644 --- a/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml +++ b/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml @@ -33,9 +33,9 @@ 3.3.0-beta.1-SNAPSHOT - 1.1.0 + 1.4.1 5.1.0 - 3.7.0 + 3.8.1 diff --git a/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml b/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml index a63c131bed..e24d465495 100644 --- a/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml +++ b/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml @@ -33,7 +33,7 @@ 3.3.0-beta.1-SNAPSHOT - 1.1.0 + 1.4.1 4.3.0 3.4.14 diff --git a/dubbo-distribution/dubbo-all/pom.xml b/dubbo-distribution/dubbo-all/pom.xml index 1e0501e7d4..ade64fe84d 100644 --- a/dubbo-distribution/dubbo-all/pom.xml +++ b/dubbo-distribution/dubbo-all/pom.xml @@ -460,10 +460,6 @@ com.alibaba.fastjson2 fastjson2 - - com.tdunning - t-digest - @@ -1004,6 +1000,12 @@ META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter + + + META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.dubbo.ByteAccessor + + @@ -1306,6 +1308,13 @@ + + + META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.BaseProviderParamParser + + + diff --git a/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidator.java b/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidator.java index f5c06a7bba..7051ab2176 100644 --- a/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidator.java +++ b/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidator.java @@ -51,7 +51,6 @@ import javax.validation.Constraint; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.validation.Validation; -import javax.validation.ValidationException; import javax.validation.ValidatorFactory; import javax.validation.groups.Default; import java.lang.annotation.Annotation; @@ -126,8 +125,8 @@ public class JValidator implements Validator { /** * try to generate methodParameterClass. * - * @param clazz interface class - * @param method invoke method + * @param clazz interface class + * @param method invoke method * @param parameterClassName generated parameterClassName * @return Class generated methodParameterClass * @throws Exception @@ -188,9 +187,9 @@ public class JValidator implements Validator { private static String generateMethodParameterClassName(Class clazz, Method method) { StringBuilder builder = new StringBuilder().append(clazz.getName()) - .append('_') - .append(toUpperMethoName(method.getName())) - .append("Parameter"); + .append('_') + .append(toUpperMethoName(method.getName())) + .append("Parameter"); Class[] parameterTypes = method.getParameterTypes(); for (Class parameterType : parameterTypes) { @@ -259,42 +258,37 @@ public class JValidator implements Validator { @Override public void validate(String methodName, Class[] parameterTypes, Object[] arguments) throws Exception { - try { - List> groups = new ArrayList<>(); - Class methodClass = methodClass(methodName); - if (methodClass != null) { - groups.add(methodClass); - } - Set> violations = new HashSet<>(); - Method method = clazz.getMethod(methodName, parameterTypes); - Class[] methodClasses; - if (method.isAnnotationPresent(MethodValidated.class)){ - methodClasses = method.getAnnotation(MethodValidated.class).value(); - groups.addAll(Arrays.asList(methodClasses)); - } - // add into default group - groups.add(0, Default.class); - groups.add(1, clazz); + List> groups = new ArrayList<>(); + Class methodClass = methodClass(methodName); + if (methodClass != null) { + groups.add(methodClass); + } + Set> violations = new HashSet<>(); + Method method = clazz.getMethod(methodName, parameterTypes); + Class[] methodClasses; + if (method.isAnnotationPresent(MethodValidated.class)) { + methodClasses = method.getAnnotation(MethodValidated.class).value(); + groups.addAll(Arrays.asList(methodClasses)); + } + // add into default group + groups.add(0, Default.class); + groups.add(1, clazz); - // convert list to array - Class[] classgroups = groups.toArray(new Class[groups.size()]); + // convert list to array + Class[] classgroups = groups.toArray(new Class[groups.size()]); - Object parameterBean = getMethodParameterBean(clazz, method, arguments); - if (parameterBean != null) { - violations.addAll(validator.validate(parameterBean, classgroups )); - } + Object parameterBean = getMethodParameterBean(clazz, method, arguments); + if (parameterBean != null) { + violations.addAll(validator.validate(parameterBean, classgroups)); + } - for (Object arg : arguments) { - validate(violations, arg, classgroups); - } + for (Object arg : arguments) { + validate(violations, arg, classgroups); + } - if (!violations.isEmpty()) { - logger.info("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations); - throw new ConstraintViolationException("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations); - } - } catch (ValidationException e) { - // only use exception's message to avoid potential serialization issue - throw new ValidationException(e.getMessage()); + if (!violations.isEmpty()) { + logger.info("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations); + throw new ConstraintViolationException("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations); } } diff --git a/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidatorNew.java b/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidatorNew.java index c52d1a85c1..fca479c76f 100644 --- a/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidatorNew.java +++ b/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidatorNew.java @@ -28,7 +28,6 @@ import jakarta.validation.Constraint; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; import jakarta.validation.Validation; -import jakarta.validation.ValidationException; import jakarta.validation.ValidatorFactory; import jakarta.validation.groups.Default; import javassist.ClassPool; @@ -126,8 +125,8 @@ public class JValidatorNew implements Validator { /** * try to generate methodParameterClass. * - * @param clazz interface class - * @param method invoke method + * @param clazz interface class + * @param method invoke method * @param parameterClassName generated parameterClassName * @return Class generated methodParameterClass * @throws Exception @@ -188,9 +187,9 @@ public class JValidatorNew implements Validator { private static String generateMethodParameterClassName(Class clazz, Method method) { StringBuilder builder = new StringBuilder().append(clazz.getName()) - .append('_') - .append(toUpperMethoName(method.getName())) - .append("Parameter"); + .append('_') + .append(toUpperMethoName(method.getName())) + .append("Parameter"); Class[] parameterTypes = method.getParameterTypes(); for (Class parameterType : parameterTypes) { @@ -259,42 +258,37 @@ public class JValidatorNew implements Validator { @Override public void validate(String methodName, Class[] parameterTypes, Object[] arguments) throws Exception { - try { - List> groups = new ArrayList<>(); - Class methodClass = methodClass(methodName); - if (methodClass != null) { - groups.add(methodClass); - } - Set> violations = new HashSet<>(); - Method method = clazz.getMethod(methodName, parameterTypes); - Class[] methodClasses; - if (method.isAnnotationPresent(MethodValidated.class)){ - methodClasses = method.getAnnotation(MethodValidated.class).value(); - groups.addAll(Arrays.asList(methodClasses)); - } - // add into default group - groups.add(0, Default.class); - groups.add(1, clazz); + List> groups = new ArrayList<>(); + Class methodClass = methodClass(methodName); + if (methodClass != null) { + groups.add(methodClass); + } + Set> violations = new HashSet<>(); + Method method = clazz.getMethod(methodName, parameterTypes); + Class[] methodClasses; + if (method.isAnnotationPresent(MethodValidated.class)) { + methodClasses = method.getAnnotation(MethodValidated.class).value(); + groups.addAll(Arrays.asList(methodClasses)); + } + // add into default group + groups.add(0, Default.class); + groups.add(1, clazz); - // convert list to array - Class[] classgroups = groups.toArray(new Class[groups.size()]); + // convert list to array + Class[] classgroups = groups.toArray(new Class[groups.size()]); - Object parameterBean = getMethodParameterBean(clazz, method, arguments); - if (parameterBean != null) { - violations.addAll(validator.validate(parameterBean, classgroups )); - } + Object parameterBean = getMethodParameterBean(clazz, method, arguments); + if (parameterBean != null) { + violations.addAll(validator.validate(parameterBean, classgroups)); + } - for (Object arg : arguments) { - validate(violations, arg, classgroups); - } + for (Object arg : arguments) { + validate(violations, arg, classgroups); + } - if (!violations.isEmpty()) { - logger.info("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations); - throw new ConstraintViolationException("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations); - } - } catch (ValidationException e) { - // only use exception's message to avoid potential serialization issue - throw new ValidationException(e.getMessage()); + if (!violations.isEmpty()) { + logger.info("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations); + throw new ConstraintViolationException("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations); } } diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java index 966ad64dc6..e35419f8c7 100644 --- a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java +++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java @@ -26,6 +26,7 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.Watch; import io.fabric8.kubernetes.client.Watcher; import io.fabric8.kubernetes.client.WatcherException; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -93,7 +94,7 @@ public class KubernetesMeshEnvListener implements MeshEnvListener { } if (action == Action.ADDED || action == Action.MODIFIED) { - String vsRule = new Yaml(new SafeConstructor()).dump(resource); + String vsRule = new Yaml(new SafeConstructor(new LoaderOptions())).dump(resource); vsAppCache.put(appName, vsRule); if (drAppCache.containsKey(appName)) { notifyListener(vsRule, appName, drAppCache.get(appName)); @@ -116,7 +117,7 @@ public class KubernetesMeshEnvListener implements MeshEnvListener { .inNamespace(namespace) .withName(appName) .get(); - vsAppCache.put(appName, new Yaml(new SafeConstructor()).dump(vsRule)); + vsAppCache.put(appName, new Yaml(new SafeConstructor(new LoaderOptions())).dump(vsRule)); } catch (Throwable ignore) { } @@ -151,7 +152,7 @@ public class KubernetesMeshEnvListener implements MeshEnvListener { } if (action == Action.ADDED || action == Action.MODIFIED) { - String drRule = new Yaml(new SafeConstructor()).dump(resource); + String drRule = new Yaml(new SafeConstructor(new LoaderOptions())).dump(resource); drAppCache.put(appName, drRule); if (vsAppCache.containsKey(appName)) { @@ -175,7 +176,7 @@ public class KubernetesMeshEnvListener implements MeshEnvListener { .inNamespace(namespace) .withName(appName) .get(); - drAppCache.put(appName, new Yaml(new SafeConstructor()).dump(drRule)); + drAppCache.put(appName, new Yaml(new SafeConstructor(new LoaderOptions())).dump(drRule)); } catch (Throwable ignore) { } diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java index e1507ea669..739a5e0584 100644 --- a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java +++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java @@ -130,7 +130,7 @@ public class KubernetesServiceDiscovery extends AbstractServiceDiscovery { .edit(pod -> new PodBuilder(pod) .editOrNewMetadata() - .addToAnnotations(KUBERNETES_PROPERTIES_KEY, JsonUtils.getJson().toJson(serviceInstance.getMetadata())) + .addToAnnotations(KUBERNETES_PROPERTIES_KEY, JsonUtils.toJson(serviceInstance.getMetadata())) .endMetadata() .build()); if (logger.isInfoEnabled()) { @@ -416,7 +416,7 @@ public class KubernetesServiceDiscovery extends AbstractServiceDiscovery { String properties = pod.getMetadata().getAnnotations().get(KUBERNETES_PROPERTIES_KEY); if (StringUtils.isNotEmpty(properties)) { - serviceInstance.getMetadata().putAll(JsonUtils.getJson().toJavaObject(properties, Map.class)); + serviceInstance.getMetadata().putAll(JsonUtils.toJavaObject(properties, Map.class)); instances.add(serviceInstance); } else { logger.warn(REGISTRY_UNABLE_FIND_SERVICE_KUBERNETES, "", "", "Unable to find Service Instance metadata in Pod Annotations. " + diff --git a/dubbo-maven-plugin/pom.xml b/dubbo-maven-plugin/pom.xml index 6f2ddf18e9..0b3302d763 100644 --- a/dubbo-maven-plugin/pom.xml +++ b/dubbo-maven-plugin/pom.xml @@ -34,13 +34,13 @@ org.apache.maven maven-plugin-api - 3.9.0 + 3.9.1 org.apache.maven maven-core - 3.9.0 + 3.9.1 provided diff --git a/dubbo-metadata/dubbo-metadata-api/pom.xml b/dubbo-metadata/dubbo-metadata-api/pom.xml index 9fc83dd32f..c951e1d36b 100644 --- a/dubbo-metadata/dubbo-metadata-api/pom.xml +++ b/dubbo-metadata/dubbo-metadata-api/pom.xml @@ -66,6 +66,25 @@ test + + org.apache.dubbo + dubbo-metrics-api + ${project.parent.version} + compile + + + org.apache.dubbo + dubbo-metrics-default + ${project.parent.version} + compile + + + org.apache.dubbo + dubbo-metrics-metadata + ${project.parent.version} + compile + + diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractCacheManager.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractCacheManager.java index 73c041a06e..1254ac5563 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractCacheManager.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractCacheManager.java @@ -140,7 +140,7 @@ public abstract class AbstractCacheManager implements Disposable { cache.lock(); try { for (Map.Entry entry : cache.entrySet()) { - properties.put(entry.getKey(), JsonUtils.getJson().toJson(entry.getValue())); + properties.put(entry.getKey(), JsonUtils.toJson(entry.getValue())); } } finally { cache.releaseLock(); diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingCacheManager.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingCacheManager.java index 2365f6ce66..4af42d4a3f 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingCacheManager.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingCacheManager.java @@ -58,7 +58,7 @@ public class MappingCacheManager extends AbstractCacheManager> { @Override protected Set toValueType(String value) { - return new HashSet<>(JsonUtils.getJson().toJavaList(value, String.class)); + return new HashSet<>(JsonUtils.toJavaList(value, String.class)); } @Override diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java index 3fe976a1de..0ec12681ab 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java @@ -16,30 +16,17 @@ */ package org.apache.dubbo.metadata; -import static org.apache.dubbo.common.utils.ClassUtils.getClassLoader; -import static org.apache.dubbo.common.utils.ClassUtils.resolveClass; public interface MetadataConstants { String KEY_SEPARATOR = ":"; String DEFAULT_PATH_TAG = "metadata"; String KEY_REVISON_PREFIX = "revision"; String META_DATA_STORE_TAG = ".metaData"; - String SERVICE_META_DATA_STORE_TAG = ".smd"; - String CONSUMER_META_DATA_STORE_TAG = ".cmd"; String METADATA_PUBLISH_DELAY_KEY = "dubbo.application.metadata.publish.delay"; int DEFAULT_METADATA_PUBLISH_DELAY = 1000; String METADATA_PROXY_TIMEOUT_KEY = "dubbo.application.metadata.proxy.delay"; int DEFAULT_METADATA_TIMEOUT_VALUE = 5000; String REPORT_CONSUMER_URL_KEY = "report-consumer-definition"; - String JAVAX_SERVLET_REQ_CLASS_NAME = "javax.servlet.ServletRequest"; - Class JAVAX_SERVLET_REQ_CLASS = resolveClass(JAVAX_SERVLET_REQ_CLASS_NAME, getClassLoader()); - String JAVAX_SERVLET_RES_CLASS_NAME = "javax.servlet.ServletResponse"; - Class JAVAX_SERVLET_RES_CLASS = resolveClass(JAVAX_SERVLET_RES_CLASS_NAME, getClassLoader()); - String JAKARTA_SERVLET_REQ_CLASS_NAME = "jakarta.servlet.ServletRequest"; - Class JAKARTA_SERVLET_REQ_CLASS = resolveClass(JAKARTA_SERVLET_REQ_CLASS_NAME, getClassLoader()); - String JAKARTA_SERVLET_RES_CLASS_NAME = "jakarta.servlet.ServletResponse"; - Class JAKARTA_SERVLET_RES_CLASS = resolveClass(JAKARTA_SERVLET_RES_CLASS_NAME, getClassLoader()); + String PATH_SEPARATOR = "/"; - String APPLICATION_FORM_URLENCODED_VALUE = "application/x-www-form-urlencoded"; - String APPLICATION_JSON_VALUE = "application/json"; } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java index 998a5e8375..f2b2bed500 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java @@ -191,7 +191,7 @@ public class MetadataInfo implements Serializable { logger.info(String.format("metadata revision changed: %s -> %s, app: %s, services: %d", this.revision, tempRevision, this.app, this.services.size())); } this.revision = tempRevision; - this.rawMetadataInfo = JsonUtils.getJson().toJson(this); + this.rawMetadataInfo = JsonUtils.toJson(this); } } return revision; diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java index 65fe2689f3..264d6a18f9 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java @@ -57,7 +57,7 @@ public class MetadataReportInstance implements Disposable { // mapping of registry id to metadata report instance, registry instances will use this mapping to find related metadata reports private final Map metadataReports = new HashMap<>(); - private ApplicationModel applicationModel; + private final ApplicationModel applicationModel; private final NopMetadataReport nopMetadataReport; public MetadataReportInstance(ApplicationModel applicationModel) { diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReport.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReport.java index dcd0bd001c..dbfd78dc55 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReport.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReport.java @@ -30,6 +30,9 @@ import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum; import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier; import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier; import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier; +import org.apache.dubbo.metrics.event.MetricsEventBus; +import org.apache.dubbo.metrics.metadata.event.MetadataEvent; +import org.apache.dubbo.rpc.model.ApplicationModel; import java.io.File; import java.io.FileInputStream; @@ -103,9 +106,11 @@ public abstract class AbstractMetadataReport implements MetadataReport { private final boolean reportMetadata; private final boolean reportDefinition; + protected ApplicationModel applicationModel; public AbstractMetadataReport(URL reportServerURL) { setUrl(reportServerURL); + applicationModel = reportServerURL.getOrDefaultApplicationModel(); boolean localCacheEnabled = reportServerURL.getParameter(REGISTRY_LOCAL_FILE_CACHE_ENABLED, true); // Start file save timer @@ -273,21 +278,31 @@ public abstract class AbstractMetadataReport implements MetadataReport { } private void storeProviderMetadataTask(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { - try { - if (logger.isInfoEnabled()) { - logger.info("store provider metadata. Identifier : " + providerMetadataIdentifier + "; definition: " + serviceDefinition); - } - allMetadataReports.put(providerMetadataIdentifier, serviceDefinition); - failedReports.remove(providerMetadataIdentifier); - String data = JsonUtils.getJson().toJson(serviceDefinition); - doStoreProviderMetadata(providerMetadataIdentifier, data); - saveProperties(providerMetadataIdentifier, data, true, !syncReport); - } catch (Exception e) { - // retry again. If failed again, throw exception. - failedReports.put(providerMetadataIdentifier, serviceDefinition); - metadataReportRetry.startRetryTask(); - logger.error(PROXY_FAILED_EXPORT_SERVICE, "", "", "Failed to put provider metadata " + providerMetadataIdentifier + " in " + serviceDefinition + ", cause: " + e.getMessage(), e); - } + + MetadataEvent metadataEvent = MetadataEvent.toServiceSubscribeEvent(applicationModel, serviceDefinition.getCanonicalName()); + MetricsEventBus.post(metadataEvent, () -> + { + boolean result = true; + try { + if (logger.isInfoEnabled()) { + logger.info("store provider metadata. Identifier : " + providerMetadataIdentifier + "; definition: " + serviceDefinition); + } + allMetadataReports.put(providerMetadataIdentifier, serviceDefinition); + failedReports.remove(providerMetadataIdentifier); + String data = JsonUtils.toJson(serviceDefinition); + doStoreProviderMetadata(providerMetadataIdentifier, data); + saveProperties(providerMetadataIdentifier, data, true, !syncReport); + } catch (Exception e) { + // retry again. If failed again, throw exception. + failedReports.put(providerMetadataIdentifier, serviceDefinition); + metadataReportRetry.startRetryTask(); + logger.error(PROXY_FAILED_EXPORT_SERVICE, "", "", "Failed to put provider metadata " + providerMetadataIdentifier + " in " + serviceDefinition + ", cause: " + e.getMessage(), e); + result = false; + } + return result; + }, aBoolean -> aBoolean + ); + } @Override @@ -307,7 +322,7 @@ public abstract class AbstractMetadataReport implements MetadataReport { allMetadataReports.put(consumerMetadataIdentifier, serviceParameterMap); failedReports.remove(consumerMetadataIdentifier); - String data = JsonUtils.getJson().toJson(serviceParameterMap); + String data = JsonUtils.toJson(serviceParameterMap); doStoreConsumerMetadata(consumerMetadataIdentifier, data); saveProperties(consumerMetadataIdentifier, data, true, !syncReport); } catch (Exception e) { @@ -359,9 +374,9 @@ public abstract class AbstractMetadataReport implements MetadataReport { @Override public void saveSubscribedData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, Set urls) { if (syncReport) { - doSaveSubscriberData(subscriberMetadataIdentifier, JsonUtils.getJson().toJson(urls)); + doSaveSubscriberData(subscriberMetadataIdentifier, JsonUtils.toJson(urls)); } else { - reportCacheExecutor.execute(() -> doSaveSubscriberData(subscriberMetadataIdentifier, JsonUtils.getJson().toJson(urls))); + reportCacheExecutor.execute(() -> doSaveSubscriberData(subscriberMetadataIdentifier, JsonUtils.toJson(urls))); } } @@ -369,7 +384,7 @@ public abstract class AbstractMetadataReport implements MetadataReport { @Override public List getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) { String content = doGetSubscribedURLs(subscriberMetadataIdentifier); - return JsonUtils.getJson().toJavaList(content, String.class); + return JsonUtils.toJavaList(content, String.class); } String getProtocol(URL url) { diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractNoAnnotatedParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractNoAnnotatedParameterProcessor.java index 10748375b9..4541e52761 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractNoAnnotatedParameterProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractNoAnnotatedParameterProcessor.java @@ -26,19 +26,20 @@ import static org.apache.dubbo.common.utils.ClassUtils.resolveClass; public abstract class AbstractNoAnnotatedParameterProcessor implements NoAnnotatedParameterRequestTagProcessor { - public void process(Parameter parameter, int parameterIndex, RestMethodMetadata restMethodMetadata) { + public boolean process(Parameter parameter, int parameterIndex, RestMethodMetadata restMethodMetadata) { MediaType mediaType = consumerContentType(); if (!contentTypeSupport(restMethodMetadata, mediaType, parameter.getType())) { - return; + return false; } boolean isFormBody = isFormContentType(restMethodMetadata); addArgInfo(parameter, parameterIndex, restMethodMetadata, isFormBody); + return true; } private boolean contentTypeSupport(RestMethodMetadata restMethodMetadata, MediaType mediaType, Class paramType) { // @RequestParam String,number param - if (mediaType.equals(MediaType.ALL_VALUE) && (String.class == paramType || Number.class.isAssignableFrom(paramType))) { + if (mediaType.equals(MediaType.ALL_VALUE) && (String.class == paramType || paramType.isPrimitive() || Number.class.isAssignableFrom(paramType))) { return true; } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java index 574a61e2c3..206fd1fad7 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java @@ -79,7 +79,8 @@ public abstract class AbstractServiceRestMetadataResolver implements ServiceRest } // for provider - return isImplementedInterface(serviceType) && isServiceAnnotationPresent(serviceType) && supports0(serviceType); + // for xml config bean && isServiceAnnotationPresent(serviceType) + return isImplementedInterface(serviceType) && supports0(serviceType); } protected final boolean isImplementedInterface(Class serviceType) { @@ -150,10 +151,10 @@ public abstract class AbstractServiceRestMetadataResolver implements ServiceRest // try the overrider method first Method serviceMethod = entry.getKey(); // If failed, it indicates the overrider method does not contain metadata , then try the declared method - if (!processRestMethodMetadata(serviceMethod, serviceType, serviceInterfaceClass, serviceRestMetadata::addRestMethodMetadata)) { + if (!processRestMethodMetadata(serviceMethod, serviceType, serviceInterfaceClass, serviceRestMetadata::addRestMethodMetadata, serviceRestMetadata)) { Method declaredServiceMethod = entry.getValue(); processRestMethodMetadata(declaredServiceMethod, serviceType, serviceInterfaceClass, - serviceRestMetadata::addRestMethodMetadata); + serviceRestMetadata::addRestMethodMetadata, serviceRestMetadata); } } } @@ -234,7 +235,8 @@ public abstract class AbstractServiceRestMetadataResolver implements ServiceRest */ protected boolean processRestMethodMetadata(Method serviceMethod, Class serviceType, Class serviceInterfaceClass, - Consumer metadataToProcess) { + Consumer metadataToProcess, + ServiceRestMetadata serviceRestMetadata) { if (!isRestCapableMethod(serviceMethod, serviceType, serviceInterfaceClass)) { return false; @@ -274,6 +276,7 @@ public abstract class AbstractServiceRestMetadataResolver implements ServiceRest // Initialize RequestMetadata RequestMetadata request = metadata.getRequest(); request.setPath(requestPath); + request.appendContextPathFromUrl(serviceRestMetadata.getContextPathFromUrl()); request.setMethod(requestMethod); request.setProduces(produces); request.setConsumes(consumes); @@ -359,9 +362,12 @@ public abstract class AbstractServiceRestMetadataResolver implements ServiceRest if (annotations == null || annotations.length == 0) { for (NoAnnotatedParameterRequestTagProcessor processor : noAnnotatedParameterRequestTagProcessors) { - processor.process(parameter, parameterIndex, metadata); + // no annotation only one default annotationType + if (processor.process(parameter, parameterIndex, metadata)) { + return; + } } - return; + } for (Annotation annotation : annotations) { diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ArgInfo.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ArgInfo.java index 599726e1e0..e4f9693083 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ArgInfo.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ArgInfo.java @@ -19,6 +19,9 @@ package org.apache.dubbo.metadata.rest; import java.lang.reflect.Parameter; +/** + * description of service method args info + */ public class ArgInfo { /** * method arg index 0,1,2,3 @@ -148,4 +151,18 @@ public class ArgInfo { this.formContentType = isFormContentType; return this; } + + @Override + public String toString() { + return "ArgInfo{" + + "index=" + index + + ", annotationNameAttribute='" + annotationNameAttribute + '\'' + + ", paramAnnotationType=" + paramAnnotationType + + ", paramType=" + paramType + + ", paramName='" + paramName + '\'' + + ", urlSplitIndex=" + urlSplitIndex + + ", defaultValue=" + defaultValue + + ", formContentType=" + formContentType + + '}'; + } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ClassPathServiceRestMetadataReader.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ClassPathServiceRestMetadataReader.java index 9530df23f0..102f37ad03 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ClassPathServiceRestMetadataReader.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ClassPathServiceRestMetadataReader.java @@ -61,7 +61,7 @@ public class ClassPathServiceRestMetadataReader implements ServiceRestMetadataRe URL resource = resources.nextElement(); InputStream inputStream = resource.openStream(); String json = IOUtils.read(inputStream, METADATA_ENCODING); - serviceRestMetadataList.addAll(JsonUtils.getJson().toJavaList(json, ServiceRestMetadata.class)); + serviceRestMetadataList.addAll(JsonUtils.toJavaList(json, ServiceRestMetadata.class)); } }); diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/NoAnnotatedParameterRequestTagProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/NoAnnotatedParameterRequestTagProcessor.java index 70f673c7e7..376e224524 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/NoAnnotatedParameterRequestTagProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/NoAnnotatedParameterRequestTagProcessor.java @@ -28,5 +28,5 @@ public interface NoAnnotatedParameterRequestTagProcessor { String defaultAnnotationClassName(RestMethodMetadata restMethodMetadata); - void process(Parameter parameter, int parameterIndex, RestMethodMetadata restMethodMetadata); + boolean process(Parameter parameter, int parameterIndex, RestMethodMetadata restMethodMetadata); } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ParamType.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ParamType.java index f517794d63..457da7ebd4 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ParamType.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ParamType.java @@ -16,8 +16,8 @@ */ package org.apache.dubbo.metadata.rest; -import org.apache.dubbo.metadata.MetadataConstants; import org.apache.dubbo.metadata.rest.tag.BodyTag; +import org.apache.dubbo.metadata.rest.tag.ParamTag; import java.util.ArrayList; import java.util.List; @@ -27,21 +27,21 @@ public enum ParamType { SpringMvcClassConstants.REQUEST_HEADER_ANNOTATION_CLASS)), PARAM(addSupportTypes(JAXRSClassConstants.QUERY_PARAM_ANNOTATION_CLASS, - SpringMvcClassConstants.REQUEST_PARAM_ANNOTATION_CLASS)), + SpringMvcClassConstants.REQUEST_PARAM_ANNOTATION_CLASS, ParamTag.class)), BODY(addSupportTypes( JAXRSClassConstants.REST_EASY_BODY_ANNOTATION_CLASS, SpringMvcClassConstants.REQUEST_BODY_ANNOTATION_CLASS, BodyTag.class)), - // TODO how to match arg type ? - REQ_OR_RES(addSupportTypes(MetadataConstants.JAKARTA_SERVLET_REQ_CLASS, - MetadataConstants.JAKARTA_SERVLET_RES_CLASS, - MetadataConstants.JAVAX_SERVLET_REQ_CLASS, - MetadataConstants.JAKARTA_SERVLET_RES_CLASS)), PATH(addSupportTypes(JAXRSClassConstants.PATH_PARAM_ANNOTATION_CLASS, SpringMvcClassConstants.PATH_VARIABLE_ANNOTATION_CLASS)), - FORM(addSupportTypes(JAXRSClassConstants.FORM_PARAM_ANNOTATION_CLASS)), + FORM(addSupportTypes(JAXRSClassConstants.FORM_PARAM_ANNOTATION_CLASS, + SpringMvcClassConstants.REQUEST_BODY_ANNOTATION_CLASS)), + + PROVIDER_BODY(addSupportTypes( + JAXRSClassConstants.REST_EASY_BODY_ANNOTATION_CLASS,JAXRSClassConstants.FORM_PARAM_ANNOTATION_CLASS, + SpringMvcClassConstants.REQUEST_BODY_ANNOTATION_CLASS, BodyTag.class)), EMPTY(addSupportTypes()); private List annotationClasses; @@ -59,16 +59,6 @@ public enum ParamType { return this.annotationClasses.contains(anno); } - public boolean isReqOrRes(Class clazz) { - for (Class annotationClass : annotationClasses) { - if (annotationClass.isAssignableFrom(clazz)) { - return true; - } - } - - return false; - } - /** * exclude null types * diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathMatcher.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathMatcher.java index 6e7dcdcbe9..12d0644ac8 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathMatcher.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathMatcher.java @@ -19,22 +19,34 @@ package org.apache.dubbo.metadata.rest; import java.util.Objects; +/** + * for http request path match + */ public class PathMatcher { private static final String SEPARATOR = "/"; private String path; - private String version; - private String group; - private Integer port; + private String version;// service version + private String group;// service group + private Integer port;// service port private String[] pathSplits; private boolean hasPathVariable; + private String contextPath; public PathMatcher(String path) { - this(path, null, null, 0); + this(path, null, null, null); } public PathMatcher(String path, String version, String group, Integer port) { this.path = path; + dealPathVariable(path); + this.version = version; + this.group = group; + this.port = (port == null || port == -1 || port == 0) ? null : port; + } + + + private void dealPathVariable(String path) { this.pathSplits = path.split(SEPARATOR); for (String pathSplit : pathSplits) { @@ -44,12 +56,9 @@ public class PathMatcher { break; } } - this.version = version; - this.group = group; - this.port = port; } - public void setPath(String path) { + private void setPath(String path) { this.path = path; } @@ -65,13 +74,39 @@ public class PathMatcher { this.port = port; } + public void setContextPath(String contextPath) { + + + contextPath = contextPathFormat(contextPath); + + + this.contextPath = contextPath; + + setPath(contextPath + path); + + dealPathVariable(path); + + } + + public static PathMatcher getInvokeCreatePathMatcher(String path, String version, String group, Integer port) { + return new PathMatcher(path, version, group, port); + } + + public boolean hasPathVariable() { + return hasPathVariable; + } + + public Integer getPort() { + return port; + } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PathMatcher that = (PathMatcher) o; - return pathEqual(that.path) && Objects.equals(version, that.version) + return pathEqual(that) + && Objects.equals(version, that.version) && Objects.equals(group, that.group) && Objects.equals(port, that.port); } @@ -80,26 +115,28 @@ public class PathMatcher { return Objects.hash(version, group, port); } - private boolean pathEqual(String path) { + private boolean pathEqual(PathMatcher pathMatcher) { + // no place hold - if (!hasPathVariable) { - return this.path.equals(path); + if (!pathMatcher.hasPathVariable) { + return this.path.equals(pathMatcher.path); } - String[] split = path.split(SEPARATOR); + String[] pathSplits = pathMatcher.pathSplits; + String[] thisPathSplits = this.pathSplits; - if (split.length != pathSplits.length) { + if (thisPathSplits.length != pathSplits.length) { return false; } for (int i = 0; i < pathSplits.length; i++) { - boolean equals = split[i].equals(pathSplits[i]); + boolean equals = thisPathSplits[i].equals(pathSplits[i]); if (equals) { continue; } else { - if (placeHoldCompare(pathSplits[i])) { + if (placeHoldCompare(pathSplits[i], thisPathSplits[i])) { continue; } else { return false; @@ -111,8 +148,8 @@ public class PathMatcher { } - private boolean placeHoldCompare(String pathSplit) { - boolean startAndEndEqual = isPlaceHold(pathSplit); + private boolean placeHoldCompare(String pathSplit, String pathToCompare) { + boolean startAndEndEqual = isPlaceHold(pathSplit) || isPlaceHold(pathToCompare); // start { end } if (!startAndEndEqual) { @@ -120,7 +157,7 @@ public class PathMatcher { } // exclude {} - boolean lengthCondition = pathSplit.length() >= 3; + boolean lengthCondition = pathSplit.length() >= 3 || pathToCompare.length() >= 3; if (!lengthCondition) { return false; @@ -134,13 +171,35 @@ public class PathMatcher { } + private String contextPathFormat(String contextPath) { + + + if (contextPath == null || contextPath.equals(SEPARATOR) || contextPath.length() == 0) { + return ""; + } + + + return pathFormat(contextPath); + } + + private String pathFormat(String path) { + if (path.startsWith(SEPARATOR)) { + return path; + } else { + return SEPARATOR + path; + } + } + + @Override public String toString() { - return "PathMather{" + + return "PathMatcher{" + "path='" + path + '\'' + ", version='" + version + '\'' + ", group='" + group + '\'' + - ", port='" + port + '\'' + + ", port=" + port + + ", hasPathVariable=" + hasPathVariable + + ", contextPath='" + contextPath + '\'' + '}'; } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RequestMetadata.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RequestMetadata.java index 77d2d4d2bb..66ff17f5ad 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RequestMetadata.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RequestMetadata.java @@ -32,6 +32,7 @@ import java.util.Set; import static java.util.Collections.unmodifiableMap; import static org.apache.dubbo.common.utils.PathUtils.normalize; +import static org.apache.dubbo.common.utils.StringUtils.SLASH; import static org.apache.dubbo.common.utils.StringUtils.isBlank; /** @@ -76,6 +77,11 @@ public class RequestMetadata implements Serializable { public void setPath(String path) { this.path = normalize(path); + + if (!path.startsWith(SLASH)) { + this.path = SLASH + path; + } + } public Map> getParams() { @@ -193,6 +199,13 @@ public class RequestMetadata implements Serializable { return this; } + public void appendContextPathFromUrl(String contextPathFromUrl) { + if (contextPathFromUrl == null) { + return; + } + setPath(contextPathFromUrl + path); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java index a2c718c563..0abc5ebd22 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java @@ -57,8 +57,6 @@ public class RestMethodMetadata implements Serializable { private Map indexToEncoded; - private ServiceRestMetadata serviceRestMetadata; - private List argInfos; private Method reflectMethod; @@ -169,15 +167,6 @@ public class RestMethodMetadata implements Serializable { this.indexToEncoded = indexToEncoded; } - - public ServiceRestMetadata getServiceRestMetadata() { - return serviceRestMetadata; - } - - public void setServiceRestMetadata(ServiceRestMetadata serviceRestMetadata) { - this.serviceRestMetadata = serviceRestMetadata; - } - public List getArgInfos() { if (argInfos == null) { argInfos = new ArrayList<>(); @@ -232,17 +221,19 @@ public class RestMethodMetadata implements Serializable { @Override public String toString() { - final StringBuilder sb = new StringBuilder("RestMethodMetadata{"); - sb.append("method=").append(method); - sb.append(", request=").append(request); - sb.append(", urlIndex=").append(urlIndex); - sb.append(", bodyIndex=").append(bodyIndex); - sb.append(", headerMapIndex=").append(headerMapIndex); - sb.append(", bodyType='").append(bodyType).append('\''); - sb.append(", indexToName=").append(indexToName); - sb.append(", formParams=").append(formParams); - sb.append(", indexToEncoded=").append(indexToEncoded); - sb.append('}'); - return sb.toString(); + return "RestMethodMetadata{" + + "method=" + method + + ", request=" + request + + ", urlIndex=" + urlIndex + + ", bodyIndex=" + bodyIndex + + ", headerMapIndex=" + headerMapIndex + + ", bodyType='" + bodyType + '\'' + + ", indexToName=" + indexToName + + ", formParams=" + formParams + + ", indexToEncoded=" + indexToEncoded + + ", argInfos=" + argInfos + + ", reflectMethod=" + reflectMethod + + ", codeStyle=" + codeStyle + + '}'; } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java index 19f0aab77b..55970c7fe7 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.metadata.rest; +import org.apache.dubbo.common.utils.PathUtils; import org.apache.dubbo.metadata.ParameterTypesComparator; import java.io.Serializable; @@ -47,12 +48,15 @@ public class ServiceRestMetadata implements Serializable { private boolean consumer; + private String contextPathFromUrl; + /** * make a distinction between mvc & resteasy */ private Class codeStyle; - private Map pathToServiceMap = new HashMap<>(); + private Map pathToServiceMapContainPathVariable = new HashMap<>(); + private Map pathToServiceMapUnContainPathVariable = new HashMap<>(); private Map> methodToServiceMap = new HashMap<>(); public ServiceRestMetadata(String serviceInterface, String version, String group, boolean consumer) { @@ -105,7 +109,6 @@ public class ServiceRestMetadata implements Serializable { } public void addRestMethodMetadata(RestMethodMetadata restMethodMetadata) { - restMethodMetadata.setServiceRestMetadata(this); PathMatcher pathMather = new PathMatcher(restMethodMetadata.getRequest().getPath(), this.getVersion(), this.getGroup(), this.getPort()); addPathToServiceMap(pathMather, restMethodMetadata); @@ -113,28 +116,57 @@ public class ServiceRestMetadata implements Serializable { getMeta().add(restMethodMetadata); } - public Map getPathToServiceMap() { - return pathToServiceMap; + + public Map getPathContainPathVariableToServiceMap() { + return pathToServiceMapContainPathVariable; + } + + public Map getPathUnContainPathVariableToServiceMap() { + return pathToServiceMapUnContainPathVariable; } public void addPathToServiceMap(PathMatcher pathMather, RestMethodMetadata restMethodMetadata) { - if (this.pathToServiceMap == null) { - this.pathToServiceMap = new HashMap<>(); - } - this.pathToServiceMap.put(pathMather, restMethodMetadata); + if (pathMather.hasPathVariable()) { + doublePathCheck(pathToServiceMapContainPathVariable, pathMather, restMethodMetadata, true); + } else { + doublePathCheck(pathToServiceMapUnContainPathVariable, pathMather, restMethodMetadata, false); + } } + private void doublePathCheck(Map pathMatcherRestMethodMetadataMap, + PathMatcher pathMather, + RestMethodMetadata restMethodMetadata, boolean containPathVariable) { + if (pathMatcherRestMethodMetadataMap.containsKey(pathMather)) { + if (containPathVariable) { + throw new IllegalArgumentException("dubbo rest metadata resolve double path error,and contain path variable is: " + + pathMather + ", rest method metadata is: " + restMethodMetadata); + + } else { + throw new IllegalArgumentException("dubbo rest metadata resolve double path error,and do not contain path variable is: " + + pathMather + ", rest method metadata is: " + restMethodMetadata); + } + } + + pathMatcherRestMethodMetadataMap.put(pathMather, restMethodMetadata); + + } + + public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; - Map pathToServiceMap = getPathToServiceMap(); - for (PathMatcher pathMather : pathToServiceMap.keySet()) { + setPort(port, getPathContainPathVariableToServiceMap()); + setPort(port, getPathUnContainPathVariableToServiceMap()); + } + + private void setPort(Integer port, Map pathToServiceMapContainPathVariable) { + for (PathMatcher pathMather : pathToServiceMapContainPathVariable.keySet()) { pathMather.setPort(port); } } @@ -168,6 +200,14 @@ public class ServiceRestMetadata implements Serializable { this.codeStyle = codeStyle; } + public String getContextPathFromUrl() { + return contextPathFromUrl == null ? "" : contextPathFromUrl; + } + + public void setContextPathFromUrl(String contextPathFromUrl) { + this.contextPathFromUrl = PathUtils.normalize(contextPathFromUrl); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/media/MediaType.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/media/MediaType.java index 8b5c96e405..2414e4ffae 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/media/MediaType.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/media/MediaType.java @@ -17,6 +17,9 @@ package org.apache.dubbo.metadata.rest.media; +import java.util.Arrays; +import java.util.List; + public enum MediaType { ALL_VALUE("*/*"), APPLICATION_JSON_VALUE("application/json"), @@ -43,4 +46,11 @@ public enum MediaType { } return stringBuilder.toString(); } + + public static List getSupportMediaTypes() { + return Arrays.asList(APPLICATION_JSON_VALUE, + APPLICATION_FORM_URLENCODED_VALUE, + TEXT_PLAIN,TEXT_XML,OCTET_STREAM); + } + } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/FormBodyNoAnnotatedProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/FormBodyNoAnnotatedProcessor.java index 60bcbc9265..38dc88663f 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/FormBodyNoAnnotatedProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/FormBodyNoAnnotatedProcessor.java @@ -19,8 +19,8 @@ package org.apache.dubbo.metadata.rest.springmvc; import org.apache.dubbo.metadata.rest.AbstractNoAnnotatedParameterProcessor; import org.apache.dubbo.metadata.rest.RestMethodMetadata; import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.metadata.rest.tag.BodyTag; -import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_BODY_ANNOTATION_CLASS_NAME; import static org.apache.dubbo.metadata.rest.media.MediaType.APPLICATION_FORM_URLENCODED_VALUE; public class FormBodyNoAnnotatedProcessor extends AbstractNoAnnotatedParameterProcessor { @@ -31,7 +31,7 @@ public class FormBodyNoAnnotatedProcessor extends AbstractNoAnnotatedParameterPr @Override public String defaultAnnotationClassName(RestMethodMetadata restMethodMetadata) { - return REQUEST_BODY_ANNOTATION_CLASS_NAME; + return BodyTag.class.getName(); } @Override diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/JsonBodyNoAnnotatedProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/JsonBodyNoAnnotatedProcessor.java index 7fc55d5538..64df7adf51 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/JsonBodyNoAnnotatedProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/JsonBodyNoAnnotatedProcessor.java @@ -19,8 +19,8 @@ package org.apache.dubbo.metadata.rest.springmvc; import org.apache.dubbo.metadata.rest.AbstractNoAnnotatedParameterProcessor; import org.apache.dubbo.metadata.rest.RestMethodMetadata; import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.metadata.rest.tag.BodyTag; -import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_BODY_ANNOTATION_CLASS_NAME; import static org.apache.dubbo.metadata.rest.media.MediaType.APPLICATION_JSON_VALUE; public class JsonBodyNoAnnotatedProcessor extends AbstractNoAnnotatedParameterProcessor { @@ -31,6 +31,6 @@ public class JsonBodyNoAnnotatedProcessor extends AbstractNoAnnotatedParameterPr @Override public String defaultAnnotationClassName(RestMethodMetadata restMethodMetadata) { - return REQUEST_BODY_ANNOTATION_CLASS_NAME; + return BodyTag.class.getName(); } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/ParamNoAnnotatedProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/ParamNoAnnotatedProcessor.java index b0591e4886..a591c28961 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/ParamNoAnnotatedProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/ParamNoAnnotatedProcessor.java @@ -21,8 +21,8 @@ import org.apache.dubbo.metadata.rest.RestMethodMetadata; import org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver; import org.apache.dubbo.metadata.rest.media.MediaType; import org.apache.dubbo.metadata.rest.tag.BodyTag; +import org.apache.dubbo.metadata.rest.tag.ParamTag; -import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_PARAM_ANNOTATION_CLASS_NAME; import static org.apache.dubbo.metadata.rest.media.MediaType.ALL_VALUE; public class ParamNoAnnotatedProcessor extends AbstractNoAnnotatedParameterProcessor { @@ -38,6 +38,6 @@ public class ParamNoAnnotatedProcessor extends AbstractNoAnnotatedParameterProce return BodyTag.class.getName(); } - return REQUEST_PARAM_ANNOTATION_CLASS_NAME; + return ParamTag.class.getName(); } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java index 135696df6a..f7e89540c4 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java @@ -55,13 +55,14 @@ public class SpringMvcServiceRestMetadataResolver extends AbstractServiceRestMet @Override protected boolean supports0(Class serviceType) { - return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS); + // class @Controller or @RequestMapping + return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS) || isAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS); } @Override protected boolean isRestCapableMethod(Method serviceMethod, Class serviceType, Class serviceInterfaceClass) { - return isAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS) || - isAnnotationPresent(serviceMethod, REQUEST_MAPPING_ANNOTATION_CLASS); + // method only match @RequestMapping + return isAnnotationPresent(serviceMethod, REQUEST_MAPPING_ANNOTATION_CLASS); } @Override diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/tag/BodyTag.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/tag/BodyTag.java index 67dd291a8e..a1038c8718 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/tag/BodyTag.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/tag/BodyTag.java @@ -16,5 +16,8 @@ */ package org.apache.dubbo.metadata.rest.tag; -public class BodyTag { +/** + * for @RequestBody class no found + */ +public interface BodyTag { } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/tag/ParamTag.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/tag/ParamTag.java new file mode 100644 index 0000000000..66249b43aa --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/tag/ParamTag.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.rest.tag; + +/** + * for @RequestParam or @QueryParam class no found + */ +public interface ParamTag { +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java index 3d0177ef78..d9f3f82b9d 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java @@ -168,13 +168,13 @@ class MetadataInfoTest { // export normal url again metadataInfo.addService(url); - System.out.println(JsonUtils.getJson().toJson(metadataInfo)); + System.out.println(JsonUtils.toJson(metadataInfo)); MetadataInfo metadataInfo2 = new MetadataInfo("demo"); // export normal url again metadataInfo2.addService(url); metadataInfo2.addService(url2); - System.out.println(JsonUtils.getJson().toJson(metadataInfo2)); + System.out.println(JsonUtils.toJson(metadataInfo2)); } @@ -218,7 +218,7 @@ class MetadataInfoTest { metadataInfo.calAndGetRevision(); - Map ret = JsonUtils.getJson().toJavaObject(metadataInfo.getContent(), Map.class); + Map ret = JsonUtils.toJavaObject(metadataInfo.getContent(), Map.class); assertNull(ret.get("content")); assertNull(ret.get("rawMetadataInfo")); } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/PathMatcherTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/PathMatcherTest.java index e2498c2b95..8e56cc202d 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/PathMatcherTest.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/PathMatcherTest.java @@ -23,10 +23,46 @@ import org.junit.jupiter.api.Test; public class PathMatcherTest { @Test - public void testPathMatcher() { - PathMatcher pathMather = new PathMatcher("/a/b/c/{path1}/d/{path2}/e"); + void testPathMatcher() { + PathMatcher pathMatherMeta = new PathMatcher("/a/b/c/{path1}/d/{path2}/e"); - PathMatcher pathMather1 = new PathMatcher("/a/b/c/1/d/2/e"); - Assertions.assertEquals(true, pathMather.equals(pathMather1)); + + PathMatcher requestPathMather = new PathMatcher("/a/b/c/1/d/2/e"); + Assertions.assertEquals(requestPathMather, pathMatherMeta); + + PathMatcher requestPathMather1 = new PathMatcher("/{c}/b/c/1/d/2/e"); + Assertions.assertEquals(requestPathMather, requestPathMather1); + + PathMatcher pathMatcher = new PathMatcher("/{d}/b/c/1/d/2/e"); + + pathMatcher.setGroup(null); + pathMatcher.setPort(null); + pathMatcher.setVersion(null); + pathMatcher.setContextPath(""); + + Assertions.assertEquals(pathMatherMeta, pathMatcher); + } + + @Test + void testEqual() { + PathMatcher pathMatherMeta = new PathMatcher("/a/b/c"); + pathMatherMeta.setContextPath("/context"); + PathMatcher pathMatherMeta1 = new PathMatcher("/a/b/d"); + + pathMatherMeta1.setContextPath("/context"); + Assertions.assertNotEquals(pathMatherMeta, pathMatherMeta1); + + pathMatherMeta1 = new PathMatcher("/a/b/c"); + pathMatherMeta1.setContextPath("/context"); + + Assertions.assertEquals(pathMatherMeta, pathMatherMeta1); + + pathMatherMeta.setContextPath("context"); + + pathMatherMeta1.setContextPath("context"); + + + Assertions.assertEquals(pathMatherMeta, pathMatherMeta1); + Assertions.assertEquals(pathMatherMeta.toString(), pathMatherMeta1.toString()); } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/TestMediaType.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/TestMediaType.java new file mode 100644 index 0000000000..f81509e0ca --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/TestMediaType.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata; + +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestMediaType { + + @Test + void testGetAll() { + Assertions.assertDoesNotThrow(() -> { + MediaType.getAllContentType(); + MediaType.getSupportMediaTypes(); + }); + } +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactoryTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactoryTest.java index 56cd80e778..7172703448 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactoryTest.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactoryTest.java @@ -46,7 +46,7 @@ class AbstractMetadataReportFactoryTest { @Override public void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { - store.put(providerMetadataIdentifier.getIdentifierKey(), JsonUtils.getJson().toJson(serviceDefinition)); + store.put(providerMetadataIdentifier.getIdentifierKey(), JsonUtils.toJson(serviceDefinition)); } @Override @@ -101,7 +101,7 @@ class AbstractMetadataReportFactoryTest { @Override public void storeConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, Map serviceParameterMap) { - store.put(consumerMetadataIdentifier.getIdentifierKey(), JsonUtils.getJson().toJson(serviceParameterMap)); + store.put(consumerMetadataIdentifier.getIdentifierKey(), JsonUtils.toJson(serviceParameterMap)); } Map store = new ConcurrentHashMap<>(); diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportTest.java index fc091b01d9..920e6b4f61 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportTest.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportTest.java @@ -29,7 +29,7 @@ import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier; import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier; import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier; import org.apache.dubbo.rpc.model.ApplicationModel; - +import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -55,13 +55,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class AbstractMetadataReportTest { private NewMetadataReport abstractMetadataReport; + private ApplicationModel applicationModel; @BeforeEach public void before() { - URL url = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic"); - abstractMetadataReport = new NewMetadataReport(url); // set the simple name of current class as the application name - ApplicationModel.defaultModel().getConfigManager().setApplication(new ApplicationConfig(getClass().getSimpleName())); + FrameworkModel frameworkModel = FrameworkModel.defaultModel(); + applicationModel = frameworkModel.newApplication(); + applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig(getClass().getSimpleName())); + + URL url = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic"); + abstractMetadataReport = new NewMetadataReport(url, applicationModel); + } @AfterEach @@ -110,7 +115,7 @@ class AbstractMetadataReportTest { void testFileExistAfterPut() throws ClassNotFoundException { //just for one method URL singleUrl = URL.valueOf("redis://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.metadata.store.InterfaceNameTestService?version=1.0.0&application=singleTest"); - NewMetadataReport singleMetadataReport = new NewMetadataReport(singleUrl); + NewMetadataReport singleMetadataReport = new NewMetadataReport(singleUrl, applicationModel); assertFalse(singleMetadataReport.file.exists()); @@ -135,7 +140,7 @@ class AbstractMetadataReportTest { String group = null; String application = "vic.retry"; URL storeUrl = URL.valueOf("retryReport://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestServiceForRetry?version=1.0.0.retry&application=vic.retry"); - RetryMetadataReport retryReport = new RetryMetadataReport(storeUrl, 2); + RetryMetadataReport retryReport = new RetryMetadataReport(storeUrl, 2, applicationModel); retryReport.metadataReportRetry.retryPeriod = 400L; URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic"); Assertions.assertNull(retryReport.metadataReportRetry.retryScheduledFuture); @@ -170,7 +175,7 @@ class AbstractMetadataReportTest { String group = null; String application = "vic.retry"; URL storeUrl = URL.valueOf("retryReport://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestServiceForRetryCancel?version=1.0.0.retrycancel&application=vic.retry"); - RetryMetadataReport retryReport = new RetryMetadataReport(storeUrl, 2); + RetryMetadataReport retryReport = new RetryMetadataReport(storeUrl, 2, applicationModel); retryReport.metadataReportRetry.retryPeriod = 150L; retryReport.metadataReportRetry.retryTimesIfNonFail = 2; @@ -259,15 +264,15 @@ class AbstractMetadataReportTest { assertEquals(3, abstractMetadataReport.store.size()); String v = abstractMetadataReport.store.get(providerMetadataIdentifier1.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); - FullServiceDefinition data = JsonUtils.getJson().toJavaObject(v, FullServiceDefinition.class); + FullServiceDefinition data = JsonUtils.toJavaObject(v, FullServiceDefinition.class); checkParam(data.getParameters(), application, version); String v2 = abstractMetadataReport.store.get(providerMetadataIdentifier2.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); - data = JsonUtils.getJson().toJavaObject(v2, FullServiceDefinition.class); + data = JsonUtils.toJavaObject(v2, FullServiceDefinition.class); checkParam(data.getParameters(), application, version + "_2"); String v3 = abstractMetadataReport.store.get(consumerMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); - Map v3Map = JsonUtils.getJson().toJavaObject(v3, Map.class); + Map v3Map = JsonUtils.toJavaObject(v3, Map.class); checkParam(v3Map, application, version + "_3"); } @@ -292,8 +297,9 @@ class AbstractMetadataReportTest { Map store = new ConcurrentHashMap<>(); - public NewMetadataReport(URL metadataReportURL) { + public NewMetadataReport(URL metadataReportURL, ApplicationModel applicationModel) { super(metadataReportURL); + this.applicationModel = applicationModel; } @Override @@ -348,9 +354,10 @@ class AbstractMetadataReportTest { int needRetryTimes; int executeTimes = 0; - public RetryMetadataReport(URL metadataReportURL, int needRetryTimes) { + public RetryMetadataReport(URL metadataReportURL, int needRetryTimes, ApplicationModel applicationModel) { super(metadataReportURL); this.needRetryTimes = needRetryTimes; + this.applicationModel = applicationModel; } @Override diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/TypeUtil.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestDoubleCheckContainsPathVariableService.java similarity index 62% rename from dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/TypeUtil.java rename to dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestDoubleCheckContainsPathVariableService.java index 9d768affc2..8f5e7c1e11 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/TypeUtil.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestDoubleCheckContainsPathVariableService.java @@ -14,25 +14,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.rpc.protocol.rest.util; +package org.apache.dubbo.metadata.rest.api; -public class TypeUtil { +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; - public static boolean isNumber(Class clazz) { - return Number.class.isAssignableFrom(clazz); - } - - public static boolean isPrimitive(Class clazz) { - return clazz.isPrimitive(); - } - - public static boolean isString(Class clazz) { - return clazz == String.class; - } - - public static boolean isNumberType(Class clazz) { - return clazz.isPrimitive() || isNumber(clazz); - } +@Path("/test") +public interface JaxrsRestDoubleCheckContainsPathVariableService { + @Path("/a/{b}") + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.TEXT_PLAIN) + @GET + String param(@QueryParam("param") String param); + @Path("/{b}/b") + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.TEXT_PLAIN) + @GET + String header(@HeaderParam("header") String header); } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestDoubleCheckService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestDoubleCheckService.java new file mode 100644 index 0000000000..ad0b3658c9 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestDoubleCheckService.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.rest.api; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +@Path("/test") +public interface JaxrsRestDoubleCheckService { + @Path("/param") + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.TEXT_PLAIN) + @GET + String param(@QueryParam("param") String param); + + @Path("/param") + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.TEXT_PLAIN) + @GET + String header(@HeaderParam("header") String header); +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestService.java index ec25773e71..ff9909ddb2 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestService.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestService.java @@ -61,5 +61,11 @@ public interface JaxrsRestService { @POST String pathVariable(@PathParam("a") String a); + @Path("/noAnno") + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.TEXT_PLAIN) + @POST + String noAnno(String a); + } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestServiceImpl.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestServiceImpl.java index b38dd83b3b..62cf3578ef 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestServiceImpl.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestServiceImpl.java @@ -47,4 +47,9 @@ public class JaxrsRestServiceImpl implements JaxrsRestService { public String pathVariable(String a) { return a; } + + @Override + public String noAnno(String a) { + return a; + } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestService.java index 00639e8c31..3a6e591df1 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestService.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestService.java @@ -38,7 +38,7 @@ public interface SpringRestService { @RequestMapping(value = "/header", method = RequestMethod.GET, consumes = MediaType.TEXT_PLAIN_VALUE, produces = MediaType.TEXT_PLAIN_VALUE) String header(@RequestHeader("header") String header); - @RequestMapping(value = "/header", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @RequestMapping(value = "/body", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) User body(@RequestBody User user); @RequestMapping(value = "/multiValue", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @@ -48,5 +48,14 @@ public interface SpringRestService { @RequestMapping(value = "/pathVariable/{a}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE) String pathVariable(@PathVariable String a); + @RequestMapping(value = "/noAnnoParam", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE, produces = MediaType.TEXT_PLAIN_VALUE) + String noAnnoParam(String a); + + @RequestMapping(value = "/noAnnoNumber", method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = MediaType.ALL_VALUE) + int noAnnoNumber(Integer b); + + + @RequestMapping(value = "/noAnnoPrimitive", method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = MediaType.ALL_VALUE) + int noAnnoPrimitive(int c); } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestServiceImpl.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestServiceImpl.java index 8262d4fe66..3176f158b8 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestServiceImpl.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestServiceImpl.java @@ -50,4 +50,19 @@ public class SpringRestServiceImpl implements SpringRestService { return a; } + @Override + public String noAnnoParam(String a) { + return a; + } + + @Override + public int noAnnoNumber(Integer b) { + return b; + } + + @Override + public int noAnnoPrimitive(int c) { + return c; + } + } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolverTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolverTest.java index 98713cfd06..32643ebab7 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolverTest.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolverTest.java @@ -95,14 +95,13 @@ class JAXRSServiceRestMetadataResolverTest { // Generated by "dubbo-metadata-processor" List jsons = Arrays.asList( - + "{\"argInfos\":[{\"annotationNameAttribute\":\"a\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.BodyTag\",\"paramName\":\"a\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"a\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/noAnno\",\"produces\":[\"text/plain\"]}}", "{\"argInfos\":[{\"annotationNameAttribute\":\"a\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"javax.ws.rs.PathParam\",\"paramName\":\"a\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":2}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"a\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/pathVariable/{a}\",\"produces\":[\"application/x-www-form-urlencoded\"]}}", - "{\"argInfos\":[{\"annotationNameAttribute\":\"map\",\"formContentType\":true,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestBody\",\"paramName\":\"map\",\"paramType\":\"javax.ws.rs.core.MultivaluedMap\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"map\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/multiValue\",\"produces\":[\"application/x-www-form-urlencoded\"]}}", - "{\"argInfos\":[{\"annotationNameAttribute\":\"user\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestBody\",\"paramName\":\"user\",\"paramType\":\"org.apache.dubbo.metadata.rest.User\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"user\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/body\",\"produces\":[\"application/json\"]}}", + "{\"argInfos\":[{\"annotationNameAttribute\":\"map\",\"formContentType\":true,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.BodyTag\",\"paramName\":\"map\",\"paramType\":\"javax.ws.rs.core.MultivaluedMap\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"map\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/multiValue\",\"produces\":[\"application/x-www-form-urlencoded\"]}}", + "{\"argInfos\":[{\"annotationNameAttribute\":\"user\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.BodyTag\",\"paramName\":\"user\",\"paramType\":\"org.apache.dubbo.metadata.rest.User\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"user\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/body\",\"produces\":[\"application/json\"]}}", "{\"argInfos\":[{\"annotationNameAttribute\":\"param\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"javax.ws.rs.QueryParam\",\"paramName\":\"param\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"param\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[],\"headers\":{},\"method\":\"GET\",\"paramNames\":[\"param\"],\"params\":{\"param\":[\"{0}\"]},\"path\":\"/param\",\"produces\":[\"text/plain\"]}}", "{\"argInfos\":[{\"annotationNameAttribute\":\"header\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"javax.ws.rs.HeaderParam\",\"paramName\":\"header\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"header\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[\"header\"],\"headers\":{\"header\":[\"{0}\"]},\"method\":\"GET\",\"paramNames\":[],\"params\":{},\"path\":\"/header\",\"produces\":[\"text/plain\"]}}" - ); ServiceRestMetadata jaxrsRestMetadata = new ServiceRestMetadata(); @@ -112,10 +111,9 @@ class JAXRSServiceRestMetadataResolverTest { List jsonsTmp = new ArrayList<>(); for (RestMethodMetadata restMethodMetadata : jaxrsMetadata.getMeta()) { - restMethodMetadata.setServiceRestMetadata(null); restMethodMetadata.setReflectMethod(null); restMethodMetadata.setMethod(null); - jsonsTmp.add(JsonUtils.getJson().toJson(restMethodMetadata)); + jsonsTmp.add(JsonUtils.toJson(restMethodMetadata)); } Comparator comparator = new Comparator() { @@ -136,7 +134,7 @@ class JAXRSServiceRestMetadataResolverTest { @Test - public void testJaxrsPathPattern() { + void testJaxrsPathPattern() { Class service = AnotherUserRestService.class; ServiceRestMetadata jaxrsRestMetadata = new ServiceRestMetadata(); jaxrsRestMetadata.setServiceInterface(service.getName()); @@ -151,7 +149,7 @@ class JAXRSServiceRestMetadataResolverTest { } - Assertions.assertEquals("/u/1", PathUtil.resolvePathVariable("u/{id : \\d+}", object.getArgInfos(), Arrays.asList(1))); + Assertions.assertEquals("/u/1", PathUtil.resolvePathVariable("/u/{id : \\d+}", object.getArgInfos(), Arrays.asList(1))); } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JaxrsRestDoubleCheckTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JaxrsRestDoubleCheckTest.java new file mode 100644 index 0000000000..97ce07d5ec --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JaxrsRestDoubleCheckTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.rest.jaxrs; + +import org.apache.dubbo.metadata.rest.ServiceRestMetadata; +import org.apache.dubbo.metadata.rest.api.JaxrsRestDoubleCheckContainsPathVariableService; +import org.apache.dubbo.metadata.rest.api.JaxrsRestDoubleCheckService; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class JaxrsRestDoubleCheckTest { + private JAXRSServiceRestMetadataResolver instance = new JAXRSServiceRestMetadataResolver(ApplicationModel.defaultModel()); + + + @Test + void testDoubleCheckException() { + + Assertions.assertThrows(IllegalArgumentException.class, () -> { + ServiceRestMetadata resolve = new ServiceRestMetadata(); + resolve.setServiceInterface(JaxrsRestDoubleCheckService.class.getName()); + instance.resolve(JaxrsRestDoubleCheckService.class, resolve); + }); + + Assertions.assertThrows(IllegalArgumentException.class, () -> { + ServiceRestMetadata resolve = new ServiceRestMetadata(); + resolve.setServiceInterface(JaxrsRestDoubleCheckContainsPathVariableService.class.getName()); + instance.resolve(JaxrsRestDoubleCheckContainsPathVariableService.class, resolve); + }); + + + } + +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java index b48c77ad1a..40f123a952 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.metadata.rest.ClassPathServiceRestMetadataReader; import org.apache.dubbo.metadata.rest.DefaultRestService; +import org.apache.dubbo.metadata.rest.PathMatcher; import org.apache.dubbo.metadata.rest.RestMethodMetadata; import org.apache.dubbo.metadata.rest.RestService; import org.apache.dubbo.metadata.rest.ServiceRestMetadata; @@ -28,6 +29,7 @@ import org.apache.dubbo.metadata.rest.api.SpringRestService; import org.apache.dubbo.metadata.rest.api.SpringRestServiceImpl; import org.apache.dubbo.rpc.model.ApplicationModel; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -35,6 +37,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -87,9 +90,11 @@ class SpringMvcServiceRestMetadataResolverTest { void testResolve(Class service) { List jsons = Arrays.asList( - + "{\"argInfos\":[{\"annotationNameAttribute\":\"b\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.ParamTag\",\"paramName\":\"b\",\"paramType\":\"java.lang.Integer\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"b\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"*/*\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/noAnnoNumber\",\"produces\":[\"*/*\"]}}", + "{\"argInfos\":[{\"annotationNameAttribute\":\"c\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.ParamTag\",\"paramName\":\"c\",\"paramType\":\"int\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"c\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"*/*\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/noAnnoPrimitive\",\"produces\":[\"*/*\"]}}", + "{\"argInfos\":[{\"annotationNameAttribute\":\"a\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.ParamTag\",\"paramName\":\"a\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"a\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/noAnnoParam\",\"produces\":[\"text/plain\"]}}", "{\"argInfos\":[{\"annotationNameAttribute\":\"\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.PathVariable\",\"paramName\":\"a\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":2}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"a\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/pathVariable/{a}\",\"produces\":[\"application/x-www-form-urlencoded\"]}}", - "{\"argInfos\":[{\"annotationNameAttribute\":\"user\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestBody\",\"paramName\":\"user\",\"paramType\":\"org.apache.dubbo.metadata.rest.User\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"user\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/header\",\"produces\":[\"application/json\"]}}", + "{\"argInfos\":[{\"annotationNameAttribute\":\"user\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestBody\",\"paramName\":\"user\",\"paramType\":\"org.apache.dubbo.metadata.rest.User\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"user\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/body\",\"produces\":[\"application/json\"]}}", "{\"argInfos\":[{\"annotationNameAttribute\":\"param\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestParam\",\"paramName\":\"param\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"param\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[],\"headers\":{},\"method\":\"GET\",\"paramNames\":[\"param\"],\"params\":{\"param\":[\"{0}\"]},\"path\":\"/param\",\"produces\":[\"text/plain\"]}}", "{\"argInfos\":[{\"annotationNameAttribute\":\"map\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestBody\",\"paramName\":\"map\",\"paramType\":\"org.springframework.util.MultiValueMap\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"map\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/multiValue\",\"produces\":[\"application/x-www-form-urlencoded\"]}}", "{\"argInfos\":[{\"annotationNameAttribute\":\"header\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestHeader\",\"paramName\":\"header\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"header\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[\"header\"],\"headers\":{\"header\":[\"{0}\"]},\"method\":\"GET\",\"paramNames\":[],\"params\":{},\"path\":\"/header\",\"produces\":[\"text/plain\"]}}" @@ -104,10 +109,9 @@ class SpringMvcServiceRestMetadataResolverTest { List jsonsTmp = new ArrayList<>(); for (RestMethodMetadata restMethodMetadata : springMetadata.getMeta()) { - restMethodMetadata.setServiceRestMetadata(null); restMethodMetadata.setReflectMethod(null); restMethodMetadata.setMethod(null); - jsonsTmp.add(JsonUtils.getJson().toJson(restMethodMetadata)); + jsonsTmp.add(JsonUtils.toJson(restMethodMetadata)); } Comparator comparator = new Comparator() { @@ -125,4 +129,34 @@ class SpringMvcServiceRestMetadataResolverTest { } } + + @Test + void testDoubleCheck() { + + ServiceRestMetadata springRestMetadata = new ServiceRestMetadata(); + springRestMetadata.setServiceInterface(SpringRestServiceImpl.class.getName()); + ServiceRestMetadata springMetadata = instance.resolve(SpringRestServiceImpl.class, springRestMetadata); + + springMetadata.setContextPathFromUrl("context"); + + Assertions.assertEquals("context", springMetadata.getContextPathFromUrl()); + + springMetadata.setContextPathFromUrl("//context"); + Assertions.assertEquals("/context", springMetadata.getContextPathFromUrl()); + springMetadata.setPort(404); + Map pathContainPathVariableToServiceMap = springMetadata.getPathContainPathVariableToServiceMap(); + + for (PathMatcher pathMatcher : pathContainPathVariableToServiceMap.keySet()) { + Assertions.assertTrue(pathMatcher.hasPathVariable()); + Assertions.assertEquals(404, pathMatcher.getPort()); + } + + + Map pathUnContainPathVariableToServiceMap = springMetadata.getPathUnContainPathVariableToServiceMap(); + + for (PathMatcher pathMatcher : pathUnContainPathVariableToServiceMap.keySet()) { + Assertions.assertFalse(pathMatcher.hasPathVariable()); + Assertions.assertEquals(404, pathMatcher.getPort()); + } + } } diff --git a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/ServiceDefinitionMetadataAnnotationProcessor.java b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/ServiceDefinitionMetadataAnnotationProcessor.java index cbeecc9073..cf8b45695e 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/ServiceDefinitionMetadataAnnotationProcessor.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/ServiceDefinitionMetadataAnnotationProcessor.java @@ -47,7 +47,7 @@ public class ServiceDefinitionMetadataAnnotationProcessor extends AbstractServic if (roundEnv.processingOver()) { ClassPathMetadataStorage writer = new ClassPathMetadataStorage(processingEnv); - writer.write(() -> JsonUtils.getJson().toJson(serviceDefinitions), "META-INF/dubbo/service-definitions.json"); + writer.write(() -> JsonUtils.toJson(serviceDefinitions), "META-INF/dubbo/service-definitions.json"); } return false; diff --git a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/rest/AbstractServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/rest/AbstractServiceRestMetadataResolver.java index c4d85e98a9..a52bd25710 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/rest/AbstractServiceRestMetadataResolver.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/rest/AbstractServiceRestMetadataResolver.java @@ -94,8 +94,8 @@ public abstract class AbstractServiceRestMetadataResolver implements ServiceRest sort(serviceMethods, ExecutableElementComparator.INSTANCE); serviceMethods.forEach(serviceMethod -> { - resolveRestMethodMetadata(processingEnv, serviceType, serviceInterfaceType, serviceMethod) - .ifPresent(serviceRestMetadata.getMeta()::add); + resolveRestMethodMetadata(processingEnv, serviceType, serviceInterfaceType, serviceMethod, serviceRestMetadata) + .ifPresent(serviceRestMetadata.getMeta()::add); }); } finally { @@ -110,7 +110,8 @@ public abstract class AbstractServiceRestMetadataResolver implements ServiceRest protected Optional resolveRestMethodMetadata(ProcessingEnvironment processingEnv, TypeElement serviceType, TypeElement serviceInterfaceType, - ExecutableElement serviceMethod) { + ExecutableElement serviceMethod, + ServiceRestMetadata serviceRestMetadata) { ExecutableElement restCapableMethod = findRestCapableMethod(processingEnv, serviceType, serviceInterfaceType, serviceMethod); @@ -150,6 +151,8 @@ public abstract class AbstractServiceRestMetadataResolver implements ServiceRest // Initialize RequestMetadata RequestMetadata request = metadata.getRequest(); request.setPath(requestPath); + request.appendContextPathFromUrl(serviceRestMetadata.getContextPathFromUrl()); + request.setMethod(requestMethod); request.setProduces(produces); request.setConsumes(consumes); @@ -237,9 +240,9 @@ public abstract class AbstractServiceRestMetadataResolver implements ServiceRest parameter.getAnnotationMirrors().forEach(annotation -> { String annotationType = annotation.getAnnotationType().toString(); parameterProcessorsMap.getOrDefault(annotationType, emptyList()) - .forEach(parameterProcessor -> { - parameterProcessor.process(annotation, parameter, parameterIndex, method, metadata); - }); + .forEach(parameterProcessor -> { + parameterProcessor.process(annotation, parameter, parameterIndex, method, metadata); + }); }); } diff --git a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/rest/ServiceRestMetadataStorage.java b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/rest/ServiceRestMetadataStorage.java index 48bf5d7197..a24f6b789f 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/rest/ServiceRestMetadataStorage.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/rest/ServiceRestMetadataStorage.java @@ -47,7 +47,7 @@ public class ServiceRestMetadataStorage { while ((len = reader.read(buf)) != -1) { stringBuilder.append(buf, 0, len); } - return JsonUtils.getJson().toJavaList(stringBuilder.toString(), ServiceRestMetadata.class); + return JsonUtils.toJavaList(stringBuilder.toString(), ServiceRestMetadata.class); } catch (IOException e) { return null; } @@ -59,7 +59,7 @@ public class ServiceRestMetadataStorage { if (serviceRestMetadata.isEmpty()) { return; } - storage.write(() -> JsonUtils.getJson().toJson(serviceRestMetadata), SERVICE_REST_METADATA_RESOURCE_PATH); + storage.write(() -> JsonUtils.toJson(serviceRestMetadata), SERVICE_REST_METADATA_RESOURCE_PATH); } } diff --git a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/rest/springmvc/SpringMvcServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/rest/springmvc/SpringMvcServiceRestMetadataResolver.java index 713bf74fa0..e33fdb3f1d 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/rest/springmvc/SpringMvcServiceRestMetadataResolver.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/rest/springmvc/SpringMvcServiceRestMetadataResolver.java @@ -64,7 +64,8 @@ public class SpringMvcServiceRestMetadataResolver extends AbstractServiceRestMet } public static boolean supports(TypeElement serviceType) { - return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS_NAME); + // class @Controller or @RequestMapping + return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS_NAME) || isAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS_NAME); } @Override @@ -131,15 +132,15 @@ public class SpringMvcServiceRestMetadataResolver extends AbstractServiceRestMet private AnnotationMirror getMappingAnnotation(Element element) { return computeIfAbsent(valueOf(element), key -> - filterFirst(getAllAnnotations(element), annotation -> { - DeclaredType annotationType = annotation.getAnnotationType(); - // try "@RequestMapping" first - if (REQUEST_MAPPING_ANNOTATION_CLASS_NAME.equals(annotationType.toString())) { - return true; - } - // try meta annotation - return isAnnotationPresent(annotationType.asElement(), REQUEST_MAPPING_ANNOTATION_CLASS_NAME); - }) + filterFirst(getAllAnnotations(element), annotation -> { + DeclaredType annotationType = annotation.getAnnotationType(); + // try "@RequestMapping" first + if (REQUEST_MAPPING_ANNOTATION_CLASS_NAME.equals(annotationType.toString())) { + return true; + } + // try meta annotation + return isAnnotationPresent(annotationType.asElement(), REQUEST_MAPPING_ANNOTATION_CLASS_NAME); + }) ); } diff --git a/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java b/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java index 70a9a1210c..c3a16d4c43 100644 --- a/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java +++ b/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java @@ -213,7 +213,7 @@ public class NacosMetadataReport extends AbstractMetadataReport { public MetadataInfo getAppMetadata(SubscriberMetadataIdentifier identifier, Map instanceMetadata) { try { String content = configService.getConfig(identifier.getApplication(), identifier.getRevision(), 3000L); - return JsonUtils.getJson().toJavaObject(content, MetadataInfo.class); + return JsonUtils.toJavaObject(content, MetadataInfo.class); } catch (NacosException e) { throw new IllegalStateException(e.getMessage(), e); } diff --git a/dubbo-metadata/dubbo-metadata-report-redis/src/test/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReportTest.java b/dubbo-metadata/dubbo-metadata-report-redis/src/test/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReportTest.java index 2b7dc9413e..0306e96050 100644 --- a/dubbo-metadata/dubbo-metadata-report-redis/src/test/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReportTest.java +++ b/dubbo-metadata/dubbo-metadata-report-redis/src/test/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReportTest.java @@ -133,7 +133,7 @@ class RedisMetadataReportTest { Assertions.assertNotNull(value); - FullServiceDefinition fullServiceDefinition = JsonUtils.getJson().toJavaObject(value, FullServiceDefinition.class); + FullServiceDefinition fullServiceDefinition = JsonUtils.toJavaObject(value, FullServiceDefinition.class); Assertions.assertEquals(fullServiceDefinition.getParameters().get("paramTest"), "redisTest"); } catch (Throwable e) { throw new RpcException("Failed to put to redis . cause: " + e.getMessage(), e); diff --git a/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport.java b/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport.java index 4231966aaa..a7cfa56dbe 100644 --- a/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport.java +++ b/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport.java @@ -158,7 +158,7 @@ public class ZookeeperMetadataReport extends AbstractMetadataReport { @Override public MetadataInfo getAppMetadata(SubscriberMetadataIdentifier identifier, Map instanceMetadata) { String content = zkClient.getContent(getNodePath(identifier)); - return JsonUtils.getJson().toJavaObject(content, MetadataInfo.class); + return JsonUtils.toJavaObject(content, MetadataInfo.class); } @Override diff --git a/dubbo-metadata/dubbo-metadata-report-zookeeper/src/test/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportTest.java b/dubbo-metadata/dubbo-metadata-report-zookeeper/src/test/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportTest.java index 183185514b..388f32d81f 100644 --- a/dubbo-metadata/dubbo-metadata-report-zookeeper/src/test/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportTest.java +++ b/dubbo-metadata/dubbo-metadata-report-zookeeper/src/test/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportTest.java @@ -100,7 +100,7 @@ class ZookeeperMetadataReportTest { fileContent = waitSeconds(fileContent, 3500, zookeeperMetadataReport.getNodePath(providerMetadataIdentifier)); Assertions.assertNotNull(fileContent); - FullServiceDefinition fullServiceDefinition = JsonUtils.getJson().toJavaObject(fileContent, FullServiceDefinition.class); + FullServiceDefinition fullServiceDefinition = JsonUtils.toJavaObject(fileContent, FullServiceDefinition.class); Assertions.assertEquals(fullServiceDefinition.getParameters().get("paramTest"), "zkTest"); } @@ -203,7 +203,7 @@ class ZookeeperMetadataReportTest { String protocol = "xxx"; URL url = generateURL(interfaceName, version, group, application); SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier(application, revision); - String r = JsonUtils.getJson().toJson(Arrays.asList(url.toString())); + String r = JsonUtils.toJson(Arrays.asList(url.toString())); zookeeperMetadataReport.doSaveSubscriberData(subscriberMetadataIdentifier, r); String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(subscriberMetadataIdentifier)); @@ -223,7 +223,7 @@ class ZookeeperMetadataReportTest { String protocol = "xxx"; URL url = generateURL(interfaceName, version, group, application); SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier(application, revision); - String r = JsonUtils.getJson().toJson(Arrays.asList(url.toString())); + String r = JsonUtils.toJson(Arrays.asList(url.toString())); zookeeperMetadataReport.doSaveSubscriberData(subscriberMetadataIdentifier, r); String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(subscriberMetadataIdentifier)); diff --git a/dubbo-metrics/dubbo-metrics-api/pom.xml b/dubbo-metrics/dubbo-metrics-api/pom.xml index 3c31ae389c..a35c238395 100644 --- a/dubbo-metrics/dubbo-metrics-api/pom.xml +++ b/dubbo-metrics/dubbo-metrics-api/pom.xml @@ -49,5 +49,10 @@ com.tdunning t-digest + + io.micrometer + micrometer-tracing-integration-test + test + diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/MetricsConstants.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/MetricsConstants.java new file mode 100644 index 0000000000..7b5c4598c0 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/MetricsConstants.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics; + +public interface MetricsConstants { + + String ATTACHMENT_KEY_SERVICE = "serviceKey"; + String ATTACHMENT_KEY_SIZE = "size"; + String ATTACHMENT_KEY_LAST_NUM_MAP = "lastNumMap"; + String ATTACHMENT_DIRECTORY_MAP = "dirNum"; + +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/ApplicationStatComposite.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/ApplicationStatComposite.java new file mode 100644 index 0000000000..087334bc09 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/ApplicationStatComposite.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.data; + +import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.metrics.model.MetricsCategory; +import org.apache.dubbo.metrics.model.MetricsSupport; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; +import org.apache.dubbo.metrics.report.MetricsExport; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +public class ApplicationStatComposite implements MetricsExport { + + private final Map> applicationNumStats = new ConcurrentHashMap<>(); + + public void init(List appKeys) { + if (CollectionUtils.isEmpty(appKeys)) { + return; + } + appKeys.forEach(appKey -> applicationNumStats.put(appKey, new ConcurrentHashMap<>())); + } + + public void incrementSize(MetricsKey metricsKey, String applicationName, int size) { + if (!applicationNumStats.containsKey(metricsKey)) { + return; + } + applicationNumStats.get(metricsKey).computeIfAbsent(applicationName, k -> new AtomicLong(0L)).getAndAdd(size); + } + + public void setApplicationKey(MetricsKey metricsKey, String applicationName, int num) { + if (!applicationNumStats.containsKey(metricsKey)) { + return; + } + applicationNumStats.get(metricsKey).computeIfAbsent(applicationName, k -> new AtomicLong(0L)).set(num); + } + + + @SuppressWarnings({"rawtypes"}) + public List export(MetricsCategory category) { + List list = new ArrayList<>(); + for (MetricsKey type : applicationNumStats.keySet()) { + Map stringAtomicLongMap = applicationNumStats.get(type); + for (String applicationName : stringAtomicLongMap.keySet()) { + list.add(convertToSample(applicationName, type, category, stringAtomicLongMap.get(applicationName))); + } + } + return list; + } + + @SuppressWarnings({"rawtypes"}) + private GaugeMetricSample convertToSample(String applicationName, MetricsKey type, MetricsCategory category, AtomicLong targetNumber) { + return new GaugeMetricSample<>(type, MetricsSupport.applicationTags(applicationName), category, targetNumber, AtomicLong::get); + } + + public Map> getApplicationNumStats() { + return applicationNumStats; + } + +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/BaseStatComposite.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/BaseStatComposite.java new file mode 100644 index 0000000000..0805c9e090 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/BaseStatComposite.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.data; + +import org.apache.dubbo.metrics.collector.MetricsCollector; +import org.apache.dubbo.metrics.model.MetricsCategory; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; + +import org.apache.dubbo.metrics.report.MetricsExport; + +import java.util.ArrayList; +import java.util.List; + + +/** + * As a data aggregator, use internal data containers calculates and classifies + * the registry data collected by {@link MetricsCollector MetricsCollector}, and + * provides an {@link MetricsExport MetricsExport} interface for exporting standard output formats. + */ +public abstract class BaseStatComposite implements MetricsExport { + + private final ApplicationStatComposite applicationStatComposite = new ApplicationStatComposite(); + private final ServiceStatComposite serviceStatComposite = new ServiceStatComposite(); + private final RtStatComposite rtStatComposite = new RtStatComposite(); + + + public BaseStatComposite() { + init(applicationStatComposite, serviceStatComposite, rtStatComposite); + } + + protected abstract void init(ApplicationStatComposite applicationStatComposite, ServiceStatComposite serviceStatComposite, RtStatComposite rtStatComposite); + + public void calcApplicationRt(String applicationName, String registryOpType, Long responseTime) { + rtStatComposite.calcApplicationRt(applicationName, registryOpType, responseTime); + } + + public void calcServiceKeyRt(String applicationName, String serviceKey, String registryOpType, Long responseTime) { + rtStatComposite.calcServiceKeyRt(applicationName, serviceKey, registryOpType, responseTime); + } + + public void setServiceKey(MetricsKey metricsKey, String applicationName, String serviceKey, int num) { + serviceStatComposite.setServiceKey(metricsKey, applicationName, serviceKey, num); + } + + public void setApplicationKey(MetricsKey metricsKey, String applicationName, int num) { + applicationStatComposite.setApplicationKey(metricsKey, applicationName, num); + } + + public void incrementApp(MetricsKey metricsKey, String applicationName, int size) { + applicationStatComposite.incrementSize(metricsKey, applicationName, size); + } + + public void incrementServiceKey(MetricsKey metricsKey, String applicationName, String attServiceKey, int size) { + serviceStatComposite.incrementServiceKey(metricsKey, applicationName, attServiceKey, size); + } + + @Override + @SuppressWarnings({"rawtypes"}) + public List export(MetricsCategory category) { + List list = new ArrayList<>(); + list.addAll(applicationStatComposite.export(category)); + list.addAll(rtStatComposite.export(category)); + list.addAll(serviceStatComposite.export(category)); + return list; + } + + public ApplicationStatComposite getApplicationStatComposite() { + return applicationStatComposite; + } + + public RtStatComposite getRtStatComposite() { + return rtStatComposite; + } +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/RtStatComposite.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/RtStatComposite.java new file mode 100644 index 0000000000..bf1bd09179 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/RtStatComposite.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.data; + +import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; +import org.apache.dubbo.metrics.model.MetricsCategory; +import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; +import org.apache.dubbo.metrics.model.container.AtomicLongContainer; +import org.apache.dubbo.metrics.model.container.LongAccumulatorContainer; +import org.apache.dubbo.metrics.model.container.LongContainer; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsPlaceType; +import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; +import org.apache.dubbo.metrics.report.MetricsExport; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.LongAccumulator; +import java.util.stream.Collectors; + +public class RtStatComposite implements MetricsExport { + + private final List> rtStats = new ArrayList<>(); + + public void init(MetricsPlaceType... placeValues) { + if (placeValues == null) { + return; + } + Arrays.stream(placeValues).forEach(metricsPlaceType -> rtStats.addAll(initStats(metricsPlaceType))); + } + + private List> initStats(MetricsPlaceType placeValue) { + List> singleRtStats = new ArrayList<>(); + singleRtStats.add(new AtomicLongContainer(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, placeValue))); + singleRtStats.add(new LongAccumulatorContainer(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, placeValue), new LongAccumulator(Long::min, Long.MAX_VALUE))); + singleRtStats.add(new LongAccumulatorContainer(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, placeValue), new LongAccumulator(Long::max, Long.MIN_VALUE))); + singleRtStats.add(new AtomicLongContainer(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, placeValue), (responseTime, longAccumulator) -> longAccumulator.addAndGet(responseTime))); + // AvgContainer is a special counter that stores the number of times but outputs function of sum/times + AtomicLongContainer avgContainer = new AtomicLongContainer(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, placeValue), (k, v) -> v.incrementAndGet()); + avgContainer.setValueSupplier(applicationName -> { + LongContainer totalContainer = rtStats.stream().filter(longContainer -> longContainer.isKeyWrapper(MetricsKey.METRIC_RT_SUM, placeValue.getType())).findFirst().get(); + AtomicLong totalRtTimes = avgContainer.get(applicationName); + AtomicLong totalRtSum = (AtomicLong) totalContainer.get(applicationName); + return totalRtSum.get() / totalRtTimes.get(); + }); + singleRtStats.add(avgContainer); + return singleRtStats; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public void calcApplicationRt(String applicationName, String registryOpType, Long responseTime) { + for (LongContainer container : rtStats.stream().filter(longContainer -> longContainer.specifyType(registryOpType)).collect(Collectors.toList())) { + Number current = (Number) ConcurrentHashMapUtils.computeIfAbsent(container, applicationName, container.getInitFunc()); + container.getConsumerFunc().accept(responseTime, current); + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public void calcServiceKeyRt(String applicationName, String serviceKey, String registryOpType, Long responseTime) { + for (LongContainer container : rtStats.stream().filter(longContainer -> longContainer.specifyType(registryOpType)).collect(Collectors.toList())) { + Number current = (Number) ConcurrentHashMapUtils.computeIfAbsent(container, applicationName + "_" + serviceKey, container.getInitFunc()); + container.getConsumerFunc().accept(responseTime, current); + } + } + + @SuppressWarnings({"rawtypes"}) + public List export(MetricsCategory category) { + List list = new ArrayList<>(); + for (LongContainer rtContainer : rtStats) { + MetricsKeyWrapper metricsKeyWrapper = rtContainer.getMetricsKeyWrapper(); + for (Map.Entry entry : rtContainer.entrySet()) { + list.add(new GaugeMetricSample<>(metricsKeyWrapper.targetKey(), metricsKeyWrapper.targetDesc(), metricsKeyWrapper.tagName(entry.getKey()), category, entry.getKey().intern(), value -> rtContainer.getValueSupplier().apply(value.intern()))); + } + } + return list; + } + + public List> getRtStats() { + return rtStats; + } + +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/ServiceStatComposite.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/ServiceStatComposite.java new file mode 100644 index 0000000000..7b46751392 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/ServiceStatComposite.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.data; + +import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.metrics.model.MetricsCategory; +import org.apache.dubbo.metrics.model.ServiceKeyMetric; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; +import org.apache.dubbo.metrics.report.MetricsExport; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +public class ServiceStatComposite implements MetricsExport { + + private final Map> serviceNumStats = new ConcurrentHashMap<>(); + + public void init(List serviceKeys) { + if (CollectionUtils.isEmpty(serviceKeys)) { + return; + } + serviceKeys.forEach(appKey -> serviceNumStats.put(appKey, new ConcurrentHashMap<>())); + } + + public void incrementServiceKey(MetricsKey metricsKey, String applicationName, String serviceKey, int size) { + if (!serviceNumStats.containsKey(metricsKey)) { + return; + } + serviceNumStats.get(metricsKey).computeIfAbsent(new ServiceKeyMetric(applicationName, serviceKey), k -> new AtomicLong(0L)).getAndAdd(size); + } + + public void setServiceKey(MetricsKey type, String applicationName, String serviceKey, int num) { + if (!serviceNumStats.containsKey(type)) { + return; + } + serviceNumStats.get(type).computeIfAbsent(new ServiceKeyMetric(applicationName, serviceKey), k -> new AtomicLong(0L)).set(num); + } + + @SuppressWarnings({"rawtypes"}) + public List export(MetricsCategory category) { + List list = new ArrayList<>(); + for (MetricsKey type : serviceNumStats.keySet()) { + Map stringAtomicLongMap = serviceNumStats.get(type); + for (ServiceKeyMetric serviceKeyMetric : stringAtomicLongMap.keySet()) { + list.add(new GaugeMetricSample<>(type, serviceKeyMetric.getTags(), category, stringAtomicLongMap, value -> value.get(serviceKeyMetric).get())); + } + } + return list; + } + +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/EmptyEvent.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/EmptyEvent.java index 3298927006..7cc8e3e1a2 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/EmptyEvent.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/EmptyEvent.java @@ -17,14 +17,16 @@ package org.apache.dubbo.metrics.event; +import org.apache.dubbo.rpc.model.ApplicationModel; + /** * EmptyEvent, do nothing. */ public class EmptyEvent extends MetricsEvent { - private static final EmptyEvent empty = new EmptyEvent(new Object()); + private static final EmptyEvent empty = new EmptyEvent(null); - public EmptyEvent(Object source) { + private EmptyEvent(ApplicationModel source) { super(source); } diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MethodEvent.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MethodEvent.java index c876070e17..3686c03197 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MethodEvent.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MethodEvent.java @@ -17,12 +17,22 @@ package org.apache.dubbo.metrics.event; +import org.apache.dubbo.metrics.model.MethodMetric; +import org.apache.dubbo.rpc.model.ApplicationModel; + public class MethodEvent extends MetricsEvent { private String type; + private final MethodMetric methodMetric; - public MethodEvent(Object source, String type) { - super(source); + public MethodEvent(ApplicationModel applicationModel, MethodMetric methodMetric, String type) { + super(applicationModel); this.type = type; + this.methodMetric = methodMetric; + setAvailable(true); + } + + public MethodMetric getMethodMetric() { + return methodMetric; } public String getType() { diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/GlobalMetricsEventMulticaster.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsDispatcher.java similarity index 91% rename from dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/GlobalMetricsEventMulticaster.java rename to dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsDispatcher.java index 28f01d5c27..eebe2011f3 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/GlobalMetricsEventMulticaster.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsDispatcher.java @@ -27,10 +27,10 @@ import java.util.List; /** * Global spi event publisher */ -public class GlobalMetricsEventMulticaster extends SimpleMetricsEventMulticaster { +public class MetricsDispatcher extends SimpleMetricsEventMulticaster { @SuppressWarnings({"rawtypes"}) - public GlobalMetricsEventMulticaster(ApplicationModel applicationModel) { + public MetricsDispatcher(ApplicationModel applicationModel) { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); ExtensionLoader extensionLoader = applicationModel.getExtensionLoader(MetricsCollector.class); if (extensionLoader != null) { diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsEvent.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsEvent.java index 0a67ac9029..091211ae40 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsEvent.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsEvent.java @@ -18,6 +18,8 @@ package org.apache.dubbo.metrics.event; import org.apache.dubbo.metrics.model.MethodMetric; +import org.apache.dubbo.metrics.model.key.TypeWrapper; +import org.apache.dubbo.rpc.model.ApplicationModel; /** * BaseMetricsEvent. @@ -27,20 +29,42 @@ public abstract class MetricsEvent { /** * Metric object. (eg. {@link MethodMetric}) */ - protected transient Object source; + protected transient ApplicationModel source; + private boolean available = true; + protected TypeWrapper typeWrapper; - public MetricsEvent(Object source) { + @SuppressWarnings({"unchecked"}) + public MetricsEvent(ApplicationModel source) { if (source == null) { - throw new IllegalArgumentException("null source"); + this.source = ApplicationModel.defaultModel(); + // Appears only in unit tests + this.available = false; + } else { + this.source = source; } - - this.source = source; } - public Object getSource() { + public void setAvailable(boolean available) { + this.available = available; + } + + public boolean isAvailable() { + return available; + } + + + public void customAfterPost(Object postResult) { + + } + + public ApplicationModel getSource() { return source; } + public boolean isAssignableFrom(Object type) { + return typeWrapper.isAssignableFrom(type); + } + public String toString() { return getClass().getName() + "[source=" + source + "]"; } @@ -57,7 +81,8 @@ public abstract class MetricsEvent { APPLICATION_INFO("APPLICATION_INFO_%s"), NETWORK_EXCEPTION("NETWORK_EXCEPTION_%s"), SERVICE_UNAVAILABLE("SERVICE_UNAVAILABLE_%s"), - CODEC_EXCEPTION("CODEC_EXCEPTION_%s"),; + CODEC_EXCEPTION("CODEC_EXCEPTION_%s"), + ; private String name; diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsEventBus.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsEventBus.java new file mode 100644 index 0000000000..dc83e994ab --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsEventBus.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.event; + +import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Dispatches events to listeners, and provides ways for listeners to register themselves. + */ +public class MetricsEventBus { + + /** + * Posts an event to all registered subscribers and only once. + * + * @param event event to post. + */ + public static void publish(MetricsEvent event) { + if (event.getSource() == null) { + return; + } + ApplicationModel applicationModel = event.getSource(); + if (applicationModel.isDestroyed()) { + return; + } + ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); + if (beanFactory.isDestroyed()) { + return; + } + MetricsDispatcher dispatcher = beanFactory.getBean(MetricsDispatcher.class); + Optional.ofNullable(dispatcher).ifPresent(d -> d.publishEvent(event)); + } + + /** + * Posts an event to all registered subscribers. + * Full lifecycle post, judging success or failure by whether there is an exception + * Loop around the event target and return the original processing result + * + * @param event event to post. + * @param targetSupplier original processing result targetSupplier + */ + public static T post(MetricsEvent event, Supplier targetSupplier) { + return post(event, targetSupplier, null); + + } + + /** + * Full lifecycle post, success and failure conditions can be customized + * + * @param event event to post. + * @param targetSupplier original processing result supplier + * @param trFunction Custom event success criteria, judged according to the returned boolean type + * @param Biz result type + * @return Biz result + */ + public static T post(MetricsEvent event, Supplier targetSupplier, Function trFunction) { + if (event.getSource() == null) { + return targetSupplier.get(); + } + ApplicationModel applicationModel = event.getSource(); + if (applicationModel.isDestroyed()) { + return targetSupplier.get(); + } + ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); + if (beanFactory.isDestroyed()) { + return targetSupplier.get(); + } + MetricsDispatcher dispatcher = beanFactory.getBean(MetricsDispatcher.class); + if (dispatcher == null) { + return targetSupplier.get(); + } + dispatcher.publishEvent(event); + T result; + if (trFunction == null) { + try { + result = targetSupplier.get(); + } catch (Throwable e) { + dispatcher.publishErrorEvent(event); + throw e; + } + event.customAfterPost(result); + dispatcher.publishFinishEvent(event); + } else { + // Custom failure status + result = targetSupplier.get(); + if (trFunction.apply(result)) { + event.customAfterPost(result); + dispatcher.publishFinishEvent(event); + } else { + dispatcher.publishErrorEvent(event); + } + } + return result; + } + + +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/RTEvent.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/RTEvent.java index eb78a4bde1..18256ffda4 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/RTEvent.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/RTEvent.java @@ -17,15 +17,20 @@ package org.apache.dubbo.metrics.event; +import org.apache.dubbo.rpc.model.ApplicationModel; + /** * RtEvent. */ public class RTEvent extends MetricsEvent { private Long rt; + private final Object metric; - public RTEvent(Object source, Long rt) { - super(source); + public RTEvent(ApplicationModel applicationModel, Object metric, Long rt) { + super(applicationModel); this.rt = rt; + this.metric = metric; + setAvailable(true); } public Long getRt() { @@ -35,4 +40,8 @@ public class RTEvent extends MetricsEvent { public void setRt(Long rt) { this.rt = rt; } + + public Object getMetric() { + return metric; + } } diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticaster.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticaster.java index 64e393cc53..70179a7445 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticaster.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticaster.java @@ -19,7 +19,6 @@ package org.apache.dubbo.metrics.event; import org.apache.dubbo.metrics.listener.MetricsLifeListener; import org.apache.dubbo.metrics.listener.MetricsListener; -import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.Collections; @@ -32,16 +31,6 @@ import java.util.function.Consumer; public class SimpleMetricsEventMulticaster implements MetricsEventMulticaster { private final List> listeners = Collections.synchronizedList(new ArrayList<>()); - private boolean available = false; - - public void setAvailable() { - this.available = true; - } - - public boolean isAvailable() { - return available; - } - @Override public void addListener(MetricsListener listener) { listeners.add(listener); @@ -53,7 +42,7 @@ public class SimpleMetricsEventMulticaster implements MetricsEventMulticaster { if (event instanceof EmptyEvent) { return; } - if (validateIfSourceInstanceOfApplicationModel(event)) return; + if (validateIfApplicationConfigExist(event)) return; for (MetricsListener listener : listeners) { if (listener.isSupport(event)) { listener.onEvent(event); @@ -61,10 +50,10 @@ public class SimpleMetricsEventMulticaster implements MetricsEventMulticaster { } } - private boolean validateIfSourceInstanceOfApplicationModel(MetricsEvent event) { - if (event.getSource() instanceof ApplicationModel) { + private boolean validateIfApplicationConfigExist(MetricsEvent event) { + if (event.getSource() != null) { // Check if exist application config - return ((ApplicationModel) event.getSource()).NotExistApplicationConfig(); + return event.getSource().NotExistApplicationConfig(); } return false; } @@ -83,12 +72,12 @@ public class SimpleMetricsEventMulticaster implements MetricsEventMulticaster { @SuppressWarnings({"rawtypes"}) private void publishTimeEvent(MetricsEvent event, Consumer consumer) { - if (validateIfSourceInstanceOfApplicationModel(event)) return; + if (validateIfApplicationConfigExist(event)) return; if (event instanceof EmptyEvent) { return; } - if (event instanceof TimeCounter) { - ((TimeCounter) event).getTimePair().end(); + if (event instanceof TimeCounterEvent) { + ((TimeCounterEvent) event).getTimePair().end(); } for (MetricsListener listener : listeners) { if (listener instanceof MetricsLifeListener && listener.isSupport(event)) { diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/TimeCounterEvent.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/TimeCounterEvent.java new file mode 100644 index 0000000000..b4a0db1322 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/TimeCounterEvent.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.event; + +import org.apache.dubbo.metrics.model.TimePair; +import org.apache.dubbo.rpc.model.ApplicationModel; + +/** + * Mark certain types of events, allow automatic recording of start and end times, and provide time pairs + */ +public abstract class TimeCounterEvent extends MetricsEvent { + + private final TimePair timePair; + + public TimeCounterEvent(ApplicationModel source) { + super(source); + this.timePair = TimePair.start(); + } + + public TimePair getTimePair() { + return timePair; + } + +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/exception/MetricsNeverHappenException.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/exception/MetricsNeverHappenException.java new file mode 100644 index 0000000000..1f5336611b --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/exception/MetricsNeverHappenException.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.exception; + +public class MetricsNeverHappenException extends RuntimeException { + + public MetricsNeverHappenException(String message) { + super(message); + } +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsListener.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsListener.java index 64b4796af3..8f0926796e 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsListener.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsListener.java @@ -25,7 +25,7 @@ import org.apache.dubbo.metrics.event.MetricsEvent; public interface MetricsListener { default boolean isSupport(MetricsEvent event) { - return true; + return event.isAvailable(); } /** diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ApplicationMetric.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ApplicationMetric.java index cc1b4abba6..ed0da87e61 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ApplicationMetric.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ApplicationMetric.java @@ -18,6 +18,7 @@ package org.apache.dubbo.metrics.model; import org.apache.dubbo.common.Version; +import org.apache.dubbo.metrics.model.key.MetricsKey; import java.util.HashMap; import java.util.Map; diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ConfigCenterMetric.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ConfigCenterMetric.java new file mode 100644 index 0000000000..a6f9987e03 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ConfigCenterMetric.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.model; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_CHANGE_TYPE; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_CONFIG_CENTER; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_KEY_KEY; +import static org.apache.dubbo.common.utils.NetUtils.getLocalHost; +import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName; + +public class ConfigCenterMetric implements Metric { + + private String applicationName; + private String key; + private String group; + private String configCenter; + private String changeType; + + public ConfigCenterMetric() { + + } + + public ConfigCenterMetric(String applicationName, String key, String group, String configCenter, String changeType) { + this.applicationName = applicationName; + this.key = key; + this.group = group; + this.configCenter = configCenter; + this.changeType = changeType; + } + + @Override + public Map getTags() { + Map tags = new HashMap<>(); + tags.put(TAG_IP, getLocalHost()); + tags.put(TAG_HOSTNAME, getLocalHostName()); + tags.put(TAG_APPLICATION_NAME, applicationName); + + tags.put(TAG_KEY_KEY, key); + tags.put(TAG_GROUP_KEY, group); + tags.put(TAG_CONFIG_CENTER, configCenter); + tags.put(TAG_CHANGE_TYPE, changeType.toLowerCase()); + + return tags; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ConfigCenterMetric that = (ConfigCenterMetric) o; + + if (!Objects.equals(applicationName, that.applicationName)) + return false; + if (!Objects.equals(key, that.key)) return false; + if (!Objects.equals(group, that.group)) return false; + if (!Objects.equals(configCenter, that.configCenter)) return false; + return Objects.equals(changeType, that.changeType); + } + + @Override + public int hashCode() { + return Objects.hash(applicationName, key, group, configCenter, changeType); + } +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsCategory.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsCategory.java index fadd27e7eb..0eecdda807 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsCategory.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsCategory.java @@ -24,7 +24,9 @@ public enum MetricsCategory { RT, QPS, REQUESTS, + APPLICATION, + CONFIGCENTER, REGISTRY, + METADATA, THREAD_POOL, - APPLICATION } diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsSupport.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsSupport.java new file mode 100644 index 0000000000..18b40fc265 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsSupport.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.model; + +import org.apache.dubbo.common.Version; +import org.apache.dubbo.metrics.exception.MetricsNeverHappenException; +import org.apache.dubbo.metrics.model.key.MetricsKey; + +import java.util.HashMap; +import java.util.Map; + +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_VERSION_KEY; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP; +import static org.apache.dubbo.common.utils.NetUtils.getLocalHost; +import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName; + +public class MetricsSupport { + + private static final String version = Version.getVersion(); + private static final String commitId = Version.getLastCommitId(); + + public static Map applicationTags(String applicationName) { + Map tags = new HashMap<>(); + tags.put(TAG_IP, getLocalHost()); + tags.put(TAG_HOSTNAME, getLocalHostName()); + tags.put(TAG_APPLICATION_NAME, applicationName); + tags.put(TAG_APPLICATION_VERSION_KEY, version); + tags.put(MetricsKey.METADATA_GIT_COMMITID_METRIC.getName(), commitId); + return tags; + } + + public static Map serviceTags(String appAndServiceName) { + String[] keys = appAndServiceName.split("_"); + if (keys.length != 2) { + throw new MetricsNeverHappenException("Error service name: " + appAndServiceName); + } + Map tags = applicationTags(keys[0]); + tags.put(TAG_INTERFACE_KEY, keys[1]); + return tags; + } +} diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/stat/ServiceKeyMetric.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ServiceKeyMetric.java similarity index 89% rename from dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/stat/ServiceKeyMetric.java rename to dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ServiceKeyMetric.java index f77fe9e5f1..94eaa30edb 100644 --- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/stat/ServiceKeyMetric.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ServiceKeyMetric.java @@ -15,9 +15,7 @@ * limitations under the License. */ -package org.apache.dubbo.metrics.registry.collector.stat; - -import org.apache.dubbo.metrics.model.Metric; +package org.apache.dubbo.metrics.model; import java.util.HashMap; import java.util.Map; @@ -41,24 +39,30 @@ public class ServiceKeyMetric implements Metric { this.serviceKey = serviceKey; } + @Override public Map getTags() { Map tags = new HashMap<>(); tags.put(TAG_IP, getLocalHost()); tags.put(TAG_HOSTNAME, getLocalHostName()); tags.put(TAG_APPLICATION_NAME, applicationName); - tags.put(TAG_INTERFACE_KEY, serviceKey); return tags; } @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } ServiceKeyMetric that = (ServiceKeyMetric) o; - if (!applicationName.equals(that.applicationName)) return false; + if (!applicationName.equals(that.applicationName)) { + return false; + } return serviceKey.equals(that.serviceKey); } diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ThreadPoolRejectMetric.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ThreadPoolRejectMetric.java new file mode 100644 index 0000000000..8706070847 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ThreadPoolRejectMetric.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.model; + +import org.apache.dubbo.common.utils.ConfigUtils; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import static org.apache.dubbo.common.utils.NetUtils.getLocalHost; +import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_PID; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_THREAD_NAME; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP; + +public class ThreadPoolRejectMetric implements Metric{ + private String applicationName; + + private String threadPoolName; + + public ThreadPoolRejectMetric(String applicationName, String threadPoolName) { + this.applicationName = applicationName; + this.threadPoolName = threadPoolName; + } + + public String getThreadPoolName() { + return threadPoolName; + } + + public void setThreadPoolName(String threadPoolName) { + this.threadPoolName = threadPoolName; + } + + public String getApplicationName() { + return applicationName; + } + + public void setApplicationName(String applicationName) { + this.applicationName = applicationName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ThreadPoolRejectMetric that = (ThreadPoolRejectMetric) o; + return Objects.equals(applicationName, that.applicationName) && + Objects.equals(threadPoolName, that.threadPoolName); + } + + @Override + public int hashCode() { + return Objects.hash(applicationName, threadPoolName); + } + + public Map getTags() { + Map tags = new HashMap<>(); + tags.put(TAG_IP, getLocalHost()); + tags.put(TAG_PID, ConfigUtils.getPid()+""); + tags.put(TAG_HOSTNAME, getLocalHostName()); + tags.put(TAG_APPLICATION_NAME, applicationName); + tags.put(TAG_THREAD_NAME, threadPoolName); + return tags; + } + +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/AtomicLongContainer.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/AtomicLongContainer.java index 45a1920bb1..73ba419ce8 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/AtomicLongContainer.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/AtomicLongContainer.java @@ -17,7 +17,8 @@ package org.apache.dubbo.metrics.model.container; -import org.apache.dubbo.metrics.model.MetricsKeyWrapper; + +import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiConsumer; diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/LongAccumulatorContainer.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/LongAccumulatorContainer.java index a3f1a21dfb..1c6952dafd 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/LongAccumulatorContainer.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/LongAccumulatorContainer.java @@ -17,7 +17,8 @@ package org.apache.dubbo.metrics.model.container; -import org.apache.dubbo.metrics.model.MetricsKeyWrapper; + +import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import java.util.concurrent.atomic.LongAccumulator; diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/LongContainer.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/LongContainer.java index 2de2d15ee9..f5dc78e6bd 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/LongContainer.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/LongContainer.java @@ -17,8 +17,8 @@ package org.apache.dubbo.metrics.model.container; -import org.apache.dubbo.metrics.model.MetricsKey; -import org.apache.dubbo.metrics.model.MetricsKeyWrapper; +import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; +import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import java.util.concurrent.ConcurrentHashMap; diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKey.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsKey.java similarity index 83% rename from dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKey.java rename to dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsKey.java index a931adcb19..0d993bb4cd 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKey.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsKey.java @@ -15,15 +15,17 @@ * limitations under the License. */ -package org.apache.dubbo.metrics.model; +package org.apache.dubbo.metrics.model.key; public enum MetricsKey { APPLICATION_METRIC_INFO("dubbo.application.info.total", "Total Application Info"), + CONFIGCENTER_METRIC_TOTAL("dubbo.configcenter.total", "Config Changed Total"), + // provider metrics key METRIC_REQUESTS("dubbo.%s.requests.total", "Total Requests"), METRIC_REQUESTS_SUCCEED("dubbo.%s.requests.succeed.total", "Total Succeed Requests"), - METRIC_REQUEST_BUSINESS_FAILED("dubbo.%s.requests.business.failed.total","Total Failed Business Requests"), + METRIC_REQUEST_BUSINESS_FAILED("dubbo.%s.requests.business.failed.total", "Total Failed Business Requests"), METRIC_REQUESTS_PROCESSING("dubbo.%s.requests.processing", "Processing Requests"), METRIC_REQUESTS_TIMEOUT("dubbo.%s.requests.timeout.total", "Total Timeout Failed Requests"), @@ -59,6 +61,12 @@ public enum MetricsKey { REGISTER_METRIC_REQUESTS("dubbo.registry.register.requests.total", "Total Register Requests"), REGISTER_METRIC_REQUESTS_SUCCEED("dubbo.registry.register.requests.succeed.total", "Succeed Register Requests"), REGISTER_METRIC_REQUESTS_FAILED("dubbo.registry.register.requests.failed.total", "Failed Register Requests"), + METRIC_RT_HISTOGRAM("dubbo.%s.rt.milliseconds.histogram", "Response Time Histogram"), + + + GENERIC_METRIC_REQUESTS("dubbo.%s.requests.total", "Total %s Requests"), + GENERIC_METRIC_REQUESTS_SUCCEED("dubbo.%s.requests.succeed.total", "Succeed %s Requests"), + GENERIC_METRIC_REQUESTS_FAILED("dubbo.%s.requests.failed.total", "Failed %s Requests"), // subscribe metrics key SUBSCRIBE_METRIC_NUM("dubbo.registry.subscribe.num.total", "Total Subscribe Num"), @@ -66,11 +74,10 @@ public enum MetricsKey { SUBSCRIBE_METRIC_NUM_FAILED("dubbo.registry.subscribe.num.failed.total", "Failed Subscribe Num"), // directory metrics key - DIRECTORY_METRIC_NUM_CURRENT("dubbo.registry.directory.num.current", "Current Directory Urls"), + DIRECTORY_METRIC_NUM_ALL("dubbo.registry.directory.num.all", "All Directory Urls"), DIRECTORY_METRIC_NUM_VALID("dubbo.registry.directory.num.valid.total", "Valid Directory Urls"), - DIRECTORY_METRIC_NUM_UN_VALID("dubbo.registry.directory.num.un_valid.total", "UnValid Directory Urls"), + DIRECTORY_METRIC_NUM_TO_RECONNECT("dubbo.registry.directory.num.to_reconnect.total", "ToReconnect Directory Urls"), DIRECTORY_METRIC_NUM_DISABLE("dubbo.registry.directory.num.disable.total", "Disable Directory Urls"), - DIRECTORY_METRIC_NUM_RECOVER_DISABLE("dubbo.registry.directory.num.recover.disable.total", "Recover Disable Directory Urls"), NOTIFY_METRIC_REQUESTS("dubbo.registry.notify.requests.total", "Total Notify Requests"), NOTIFY_METRIC_NUM_LAST("dubbo.registry.notify.num.last", "Last Notify Nums"), @@ -81,6 +88,7 @@ public enum MetricsKey { THREAD_POOL_ACTIVE_SIZE("dubbo.thread.pool.active.size", "Thread Pool Active Size"), THREAD_POOL_THREAD_COUNT("dubbo.thread.pool.thread.count", "Thread Pool Thread Count"), THREAD_POOL_QUEUE_SIZE("dubbo.thread.pool.queue.size", "Thread Pool Queue Size"), + THREAD_POOL_THREAD_REJECT_COUNT("dubbo.thread.pool.reject.thread.count", "Thread Pool Reject Thread Count"), // metadata push metrics key METADATA_PUSH_METRIC_NUM("dubbo.metadata.push.num.total", "Total Push Num"), @@ -101,7 +109,14 @@ public enum MetricsKey { SERVICE_SUBSCRIBE_METRIC_NUM("dubbo.registry.subscribe.service.num.total", "Total Service-Level Subscribe Num"), SERVICE_SUBSCRIBE_METRIC_NUM_SUCCEED("dubbo.registry.subscribe.service.num.succeed.total", "Succeed Service-Level Num"), SERVICE_SUBSCRIBE_METRIC_NUM_FAILED("dubbo.registry.subscribe.service.num.failed.total", "Failed Service-Level Num"), - METADATA_GIT_COMMITID_METRIC("git.commit.id","Git Commit Id Metrics"); + // store provider metadata service key + STORE_PROVIDER_METADATA("dubbo.metadata.store.provider.total", "Store Provider Metadata"), + + STORE_PROVIDER_METADATA_SUCCEED("dubbo.metadata.store.provider.succeed.total", "Succeed Store Provider Metadata"), + + STORE_PROVIDER_METADATA_FAILED("dubbo.metadata.store.provider.failed.total", "Failed Store Provider Metadata"), + METADATA_GIT_COMMITID_METRIC("git.commit.id", "Git Commit Id Metrics"), + // consumer metrics key ; @@ -116,6 +131,12 @@ public enum MetricsKey { return String.format(name, type); } + + public final MetricsKey formatName(String type) { + this.name = String.format(name, type); + return this; + } + public final String getDescription() { return this.description; } diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKeyWrapper.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsKeyWrapper.java similarity index 63% rename from dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKeyWrapper.java rename to dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsKeyWrapper.java index 54b791e5a8..d734328e48 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKeyWrapper.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsKeyWrapper.java @@ -15,23 +15,40 @@ * limitations under the License. */ -package org.apache.dubbo.metrics.model; +package org.apache.dubbo.metrics.model.key; + +import org.apache.dubbo.metrics.model.MetricsSupport; + +import java.util.Map; /** * Let {@link MetricsKey MetricsKey} output dynamic, custom string content */ public class MetricsKeyWrapper { - private final String type; + /** + * Metrics key when exporting + */ private final MetricsKey metricsKey; + + private final MetricsPlaceType placeType; + public MetricsKeyWrapper(String type, MetricsKey metricsKey) { - this.type = type; + this(metricsKey, MetricsPlaceType.of(type, MetricsLevel.APP)); + } + + public MetricsKeyWrapper(MetricsKey metricsKey, MetricsPlaceType placeType) { this.metricsKey = metricsKey; + this.placeType = placeType; + } + + public MetricsPlaceType getPlaceType() { + return placeType; } public String getType() { - return type; + return getPlaceType().getType(); } public MetricsKey getMetricsKey() { @@ -42,9 +59,13 @@ public class MetricsKeyWrapper { return metricsKey == getMetricsKey() && registryOpType.equals(getType()); } + public boolean isServiceLevel() { + return getPlaceType().getMetricsLevel().equals(MetricsLevel.SERVICE); + } + public String targetKey() { try { - return String.format(metricsKey.getName(), type); + return String.format(metricsKey.getName(), getType()); } catch (Exception ignore) { return metricsKey.getName(); } @@ -52,10 +73,13 @@ public class MetricsKeyWrapper { public String targetDesc() { try { - return String.format(metricsKey.getDescription(), type); + return String.format(metricsKey.getDescription(), getType()); } catch (Exception ignore) { return metricsKey.getDescription(); } } + public Map tagName(String key) { + return isServiceLevel() ? MetricsSupport.serviceTags(key) : MetricsSupport.applicationTags(key); + } } diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/SpringmvcDemoService.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsLevel.java similarity index 90% rename from dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/SpringmvcDemoService.java rename to dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsLevel.java index 18726a05d1..aa86ad4946 100644 --- a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/SpringmvcDemoService.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsLevel.java @@ -14,8 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.test.common.api; +package org.apache.dubbo.metrics.model.key; -public interface SpringmvcDemoService { +public enum MetricsLevel { + APP,SERVICE } diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsPlaceType.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsPlaceType.java new file mode 100644 index 0000000000..744cd55007 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsPlaceType.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.model.key; + +public class MetricsPlaceType { + + private final String type; + private final MetricsLevel metricsLevel; + + private MetricsPlaceType(String type, MetricsLevel metricsLevel) { + this.type = type; + this.metricsLevel = metricsLevel; + } + + public static MetricsPlaceType of(String type, MetricsLevel metricsLevel) { + return new MetricsPlaceType(type, metricsLevel); + } + + public String getType() { + return type; + } + + public MetricsLevel getMetricsLevel() { + return metricsLevel; + } +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/TypeWrapper.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/TypeWrapper.java new file mode 100644 index 0000000000..643e063608 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/TypeWrapper.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.model.key; + +import org.apache.dubbo.common.utils.Assert; + +public class TypeWrapper { + private final MetricsLevel level; + private final Object postType; + private final Object finishType; + private final Object errorType; + + public TypeWrapper(MetricsLevel level, Object postType, Object finishType, Object errorType) { + this.level = level; + this.postType = postType; + this.finishType = finishType; + this.errorType = errorType; + } + + public MetricsLevel getLevel() { + return level; + } + + public Object getErrorType() { + return errorType; + } + + public boolean isAssignableFrom(Object type) { + Assert.notNull(type, "Type can not be null"); + return type.equals(postType) || type.equals(finishType) || type.equals(errorType); + } +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/sample/CounterMetricSample.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/sample/CounterMetricSample.java new file mode 100644 index 0000000000..46ce0d50f9 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/sample/CounterMetricSample.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metrics.model.sample; + +import org.apache.dubbo.metrics.model.MetricsCategory; + +import java.util.Map; + +public class CounterMetricSample extends MetricSample { + + private final T value; + + public CounterMetricSample(String name, String description, Map tags, + MetricsCategory category, T value ) { + super(name, description, tags, Type.COUNTER, category); + this.value = value; + } + + public CounterMetricSample(String name, String description, Map tags, MetricsCategory category, + String baseUnit, T value) { + super(name, description, tags, Type.COUNTER, category, baseUnit); + this.value = value; + } + + public T getValue() { + return value; + } +} diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/sample/GaugeMetricSample.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/sample/GaugeMetricSample.java index 63d4c5c70d..b795207070 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/sample/GaugeMetricSample.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/sample/GaugeMetricSample.java @@ -18,7 +18,7 @@ package org.apache.dubbo.metrics.model.sample; import org.apache.dubbo.metrics.model.MetricsCategory; -import org.apache.dubbo.metrics.model.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsKey; import java.util.Map; import java.util.Objects; diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/AbstractDefaultDubboObservationConvention.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/AbstractDefaultDubboObservationConvention.java index 3a542fef86..8d8e963868 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/AbstractDefaultDubboObservationConvention.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/AbstractDefaultDubboObservationConvention.java @@ -25,9 +25,9 @@ import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.support.RpcUtils; -import static org.apache.dubbo.metrics.observation.DubboObservation.LowCardinalityKeyNames.RPC_METHOD; -import static org.apache.dubbo.metrics.observation.DubboObservation.LowCardinalityKeyNames.RPC_SERVICE; -import static org.apache.dubbo.metrics.observation.DubboObservation.LowCardinalityKeyNames.RPC_SYSTEM; +import static org.apache.dubbo.metrics.observation.DubboObservationDocumentation.LowCardinalityKeyNames.RPC_METHOD; +import static org.apache.dubbo.metrics.observation.DubboObservationDocumentation.LowCardinalityKeyNames.RPC_SERVICE; +import static org.apache.dubbo.metrics.observation.DubboObservationDocumentation.LowCardinalityKeyNames.RPC_SYSTEM; class AbstractDefaultDubboObservationConvention { KeyValues getLowCardinalityKeyValues(Invocation invocation) { diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DefaultDubboClientObservationConvention.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DefaultDubboClientObservationConvention.java index ea31a526a5..91e88da2a3 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DefaultDubboClientObservationConvention.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DefaultDubboClientObservationConvention.java @@ -16,16 +16,17 @@ */ package org.apache.dubbo.metrics.observation; -import java.util.List; - -import io.micrometer.common.KeyValues; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcContextAttachment; -import static org.apache.dubbo.metrics.observation.DubboObservation.LowCardinalityKeyNames.NET_PEER_NAME; -import static org.apache.dubbo.metrics.observation.DubboObservation.LowCardinalityKeyNames.NET_PEER_PORT; +import io.micrometer.common.KeyValues; + +import java.util.List; + +import static org.apache.dubbo.metrics.observation.DubboObservationDocumentation.LowCardinalityKeyNames.NET_PEER_NAME; +import static org.apache.dubbo.metrics.observation.DubboObservationDocumentation.LowCardinalityKeyNames.NET_PEER_PORT; /** * Default implementation of the {@link DubboClientObservationConvention}. @@ -34,7 +35,11 @@ public class DefaultDubboClientObservationConvention extends AbstractDefaultDubb /** * Singleton instance of {@link DefaultDubboClientObservationConvention}. */ - public static final DubboClientObservationConvention INSTANCE = new DefaultDubboClientObservationConvention(); + private static final DubboClientObservationConvention INSTANCE = new DefaultDubboClientObservationConvention(); + + public static DubboClientObservationConvention getInstance() { + return INSTANCE; + } @Override public String getName() { diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DefaultDubboServerObservationConvention.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DefaultDubboServerObservationConvention.java index efb85f515e..adcebdbdac 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DefaultDubboServerObservationConvention.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DefaultDubboServerObservationConvention.java @@ -26,7 +26,11 @@ public class DefaultDubboServerObservationConvention extends AbstractDefaultDubb /** * Singleton instance of {@link DefaultDubboServerObservationConvention}. */ - public static final DubboServerObservationConvention INSTANCE = new DefaultDubboServerObservationConvention(); + private static final DubboServerObservationConvention INSTANCE = new DefaultDubboServerObservationConvention(); + + public static DubboServerObservationConvention getInstance() { + return INSTANCE; + } @Override public String getName() { diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DubboObservation.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DubboObservationDocumentation.java similarity index 96% rename from dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DubboObservation.java rename to dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DubboObservationDocumentation.java index ff4b1575ff..855a2e01e1 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DubboObservation.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/observation/DubboObservationDocumentation.java @@ -17,6 +17,7 @@ package org.apache.dubbo.metrics.observation; import io.micrometer.common.docs.KeyName; +import io.micrometer.common.lang.NonNullApi; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationConvention; import io.micrometer.observation.docs.ObservationDocumentation; @@ -24,7 +25,7 @@ import io.micrometer.observation.docs.ObservationDocumentation; /** * Documentation of Dubbo observations. */ -public enum DubboObservation implements ObservationDocumentation { +public enum DubboObservationDocumentation implements ObservationDocumentation { /** * Server side Dubbo RPC Observation. @@ -58,6 +59,7 @@ public enum DubboObservation implements ObservationDocumentation { }; + @NonNullApi enum LowCardinalityKeyNames implements KeyName { /** diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/report/MetricsExport.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/report/MetricsExport.java index 1c230545a6..82db29a1cc 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/report/MetricsExport.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/report/MetricsExport.java @@ -17,6 +17,7 @@ package org.apache.dubbo.metrics.report; +import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import java.util.List; @@ -28,12 +29,8 @@ import java.util.List; public interface MetricsExport { /** - * Cumulative statistics. + * export all. */ - List exportNumMetrics(); + List export(MetricsCategory category); - /** - * Response time statistics at different latitudes - */ - List exportRtMetrics(); } diff --git a/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticasterTest.java b/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticasterTest.java index 15f6e66f23..394034b3bb 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticasterTest.java +++ b/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticasterTest.java @@ -17,30 +17,43 @@ package org.apache.dubbo.metrics.event; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.metrics.listener.MetricsLifeListener; import org.apache.dubbo.metrics.listener.MetricsListener; +import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; + public class SimpleMetricsEventMulticasterTest { private SimpleMetricsEventMulticaster eventMulticaster; - private Object[] obj; - MetricsEvent requestEvent; + private Object[] objects; + private final Object obj = new Object(); + private MetricsEvent requestEvent; @BeforeEach public void setup() { eventMulticaster = new SimpleMetricsEventMulticaster(); - obj = new Object[]{new Object()}; + objects = new Object[]{obj}; eventMulticaster.addListener(new MetricsListener() { @Override public void onEvent(MetricsEvent event) { - obj[0] = new Object(); + objects[0] = new Object(); } }); - requestEvent = new RequestEvent(obj[0], MetricsEvent.Type.TOTAL); - + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + ConfigManager configManager = new ConfigManager(applicationModel); + configManager.setApplication(applicationConfig); + applicationModel.setConfigManager(configManager); + requestEvent = new MetricsEvent(applicationModel) { + }; } @@ -48,14 +61,9 @@ public class SimpleMetricsEventMulticasterTest { void testPublishEvent() { // emptyEvent do nothing - MetricsEvent emptyEvent = new EmptyEvent(obj[0]); + MetricsEvent emptyEvent = EmptyEvent.instance(); eventMulticaster.publishEvent(emptyEvent); - Assertions.assertSame(emptyEvent.getSource(), obj[0]); - - // not empty Event change obj[] - MetricsEvent requestEvent = new RequestEvent(obj[0], MetricsEvent.Type.TOTAL); - eventMulticaster.publishEvent(requestEvent); - Assertions.assertNotSame(requestEvent.getSource(), obj[0]); + Assertions.assertSame(obj, objects[0]); } @@ -64,7 +72,7 @@ public class SimpleMetricsEventMulticasterTest { //do nothing with no MetricsLifeListener eventMulticaster.publishFinishEvent(requestEvent); - Assertions.assertSame(requestEvent.getSource(), obj[0]); + Assertions.assertSame(obj, objects[0]); //do onEventFinish with MetricsLifeListener eventMulticaster.addListener((new MetricsLifeListener() { @@ -76,7 +84,7 @@ public class SimpleMetricsEventMulticasterTest { @Override public void onEventFinish(MetricsEvent event) { - obj[0] = new Object(); + objects[0] = new Object(); } @Override @@ -85,12 +93,8 @@ public class SimpleMetricsEventMulticasterTest { } })); eventMulticaster.publishFinishEvent(requestEvent); - Assertions.assertNotSame(requestEvent.getSource(), obj[0]); + Assertions.assertNotSame(obj, objects[0]); } - @Test - void testPublishErrorEvent() { - - } } diff --git a/dubbo-metrics/dubbo-metrics-default/pom.xml b/dubbo-metrics/dubbo-metrics-default/pom.xml index bc4165d765..87ec6a3b9b 100644 --- a/dubbo-metrics/dubbo-metrics-default/pom.xml +++ b/dubbo-metrics/dubbo-metrics-default/pom.xml @@ -46,11 +46,6 @@ micrometer-tracing-integration-test test - - org.apache.dubbo - dubbo-cluster - ${project.parent.version} - compile - + diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/DubboMetrics.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/DubboMetrics.java index 00351aedcc..ab38c121a5 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/DubboMetrics.java +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/DubboMetrics.java @@ -19,7 +19,6 @@ package org.apache.dubbo.metrics; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.MeterBinder; import io.micrometer.core.instrument.composite.CompositeMeterRegistry; -import org.apache.dubbo.metrics.report.AbstractMetricsReporter; public class DubboMetrics implements MeterBinder { @@ -29,7 +28,7 @@ public class DubboMetrics implements MeterBinder { @Override public void bindTo(MeterRegistry registry) { globalRegistry = registry; - CompositeMeterRegistry compositeRegistry = AbstractMetricsReporter.compositeRegistry; + CompositeMeterRegistry compositeRegistry = MetricsGlobalRegistry.getCompositeRegistry(); if (compositeRegistry != null) { compositeRegistry.add(registry); } diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/MetricsGlobalRegistry.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/MetricsGlobalRegistry.java new file mode 100644 index 0000000000..cc6c824c18 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/MetricsGlobalRegistry.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics; + +import io.micrometer.core.instrument.composite.CompositeMeterRegistry; + +public class MetricsGlobalRegistry { + + private static final CompositeMeterRegistry compositeRegistry = new CompositeMeterRegistry(); + + public static CompositeMeterRegistry getCompositeRegistry() { + return compositeRegistry; + } +} diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/MetricsScopeModelInitializer.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/MetricsScopeModelInitializer.java index f8236f60d2..15b2806cc6 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/MetricsScopeModelInitializer.java +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/MetricsScopeModelInitializer.java @@ -19,7 +19,7 @@ package org.apache.dubbo.metrics; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; +import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; @@ -36,7 +36,7 @@ public class MetricsScopeModelInitializer implements ScopeModelInitializer { public void initializeApplicationModel(ApplicationModel applicationModel) { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); beanFactory.registerBean(DefaultMetricsCollector.class); - beanFactory.registerBean(GlobalMetricsEventMulticaster.class); + beanFactory.registerBean(MetricsDispatcher.class); } @Override diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollector.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollector.java index 664fa371de..286fa1dcd0 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollector.java +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollector.java @@ -28,7 +28,7 @@ import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.event.RTEvent; import org.apache.dubbo.metrics.listener.MetricsListener; import org.apache.dubbo.metrics.model.MethodMetric; -import org.apache.dubbo.metrics.model.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; @@ -66,7 +66,7 @@ public class AggregateMetricsCollector implements MetricsCollector, MetricsListe this.applicationModel = applicationModel; ConfigManager configManager = applicationModel.getApplicationConfigManager(); MetricsConfig config = configManager.getMetrics().orElse(null); - if (config != null && config.getAggregation() != null && Boolean.TRUE.equals(config.getAggregation().getEnabled())) { + if (config != null && config.getAggregation() != null && (config.getAggregation().getEnabled() == null || Boolean.TRUE.equals(config.getAggregation().getEnabled()))) { // only registered when aggregation is enabled. registerListener(); @@ -86,7 +86,7 @@ public class AggregateMetricsCollector implements MetricsCollector, MetricsListe } private void onRTEvent(RTEvent event) { - MethodMetric metric = (MethodMetric) event.getSource(); + MethodMetric metric = (MethodMetric) event.getMetric(); Long responseTime = event.getRt(); TimeWindowQuantile quantile = ConcurrentHashMapUtils.computeIfAbsent(rt, metric, k -> new TimeWindowQuantile(DEFAULT_COMPRESSION, bucketNum, timeWindowSeconds)); quantile.add(responseTime); @@ -94,7 +94,7 @@ public class AggregateMetricsCollector implements MetricsCollector, MetricsListe private void onRequestEvent(MethodEvent event) { - MethodMetric metric = (MethodMetric) event.getSource(); + MethodMetric metric = event.getMethodMetric(); String type = event.getType(); diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/ConfigCenterMetricsCollector.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/ConfigCenterMetricsCollector.java new file mode 100644 index 0000000000..5fa34e4d42 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/ConfigCenterMetricsCollector.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.collector; + +import org.apache.dubbo.common.config.configcenter.ConfigChangeType; +import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; +import org.apache.dubbo.metrics.model.ConfigCenterMetric; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; +import org.apache.dubbo.metrics.model.sample.MetricSample; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_METRICS_CONFIGCENTER_ENABLE; +import static org.apache.dubbo.metrics.model.MetricsCategory.CONFIGCENTER; + +public class ConfigCenterMetricsCollector implements MetricsCollector { + + private boolean collectEnabled = true; + private final ApplicationModel applicationModel; + + private final Map updatedMetrics = new ConcurrentHashMap<>(); + + public ConfigCenterMetricsCollector(ApplicationModel applicationModel) { + this.applicationModel = applicationModel; + // default is true, disable when config false + if ("false".equals(System.getProperty(DUBBO_METRICS_CONFIGCENTER_ENABLE))) { + collectEnabled = false; + } + } + + public void setCollectEnabled(Boolean collectEnabled) { + if (collectEnabled != null) { + this.collectEnabled = collectEnabled; + } + } + + @Override + public boolean isCollectEnabled() { + return collectEnabled; + } + + public void increase4Initialized(String key, String group, String protocol, String applicationName, int count) { + if (!isCollectEnabled()) { + return; + } + if (count <= 0) { + return; + } + ConfigCenterMetric metric = new ConfigCenterMetric(applicationName, key, group, protocol, ConfigChangeType.ADDED.name()); + AtomicLong aLong = updatedMetrics.computeIfAbsent(metric, k -> new AtomicLong(0L)); + aLong.addAndGet(count); + } + + public void increaseUpdated(String protocol, String applicationName, ConfigChangedEvent event) { + if (!isCollectEnabled()) { + return; + } + ConfigCenterMetric metric = new ConfigCenterMetric(applicationName, event.getKey(), event.getGroup(), protocol, event.getChangeType().name()); + AtomicLong count = updatedMetrics.computeIfAbsent(metric, k -> new AtomicLong(0L)); + count.incrementAndGet(); + } + + @Override + public List collect() { + // Add metrics to reporter + List list = new ArrayList<>(); + if (!isCollectEnabled()) { + return list; + } + collect(list); + return list; + } + + private void collect(List list) { + updatedMetrics.forEach((k, v) -> list.add(new GaugeMetricSample<>(MetricsKey.CONFIGCENTER_METRIC_TOTAL, k.getTags(), CONFIGCENTER, v, AtomicLong::get))); + } + +} diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/DefaultMetricsCollector.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/DefaultMetricsCollector.java index 059e4abca7..953cc075e6 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/DefaultMetricsCollector.java +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/DefaultMetricsCollector.java @@ -16,7 +16,6 @@ */ package org.apache.dubbo.metrics.collector; - import org.apache.dubbo.metrics.collector.sample.MethodMetricsSampler; import org.apache.dubbo.metrics.collector.sample.MetricsCountSampleConfigurer; import org.apache.dubbo.metrics.collector.sample.MetricsSampler; @@ -26,16 +25,13 @@ import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster; import org.apache.dubbo.metrics.listener.MetricsListener; import org.apache.dubbo.metrics.model.ApplicationMetric; -import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; +import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; - import java.util.ArrayList; import java.util.List; -import java.util.concurrent.atomic.AtomicLong; - import static org.apache.dubbo.metrics.model.MetricsCategory.APPLICATION; -import static org.apache.dubbo.metrics.model.MetricsKey.APPLICATION_METRIC_INFO; +import static org.apache.dubbo.metrics.model.key.MetricsKey.APPLICATION_METRIC_INFO; /** * Default implementation of {@link MetricsCollector} @@ -43,6 +39,8 @@ import static org.apache.dubbo.metrics.model.MetricsKey.APPLICATION_METRIC_INFO; public class DefaultMetricsCollector implements MetricsCollector { private boolean collectEnabled = false; + + private volatile boolean threadpoolCollectEnabled=false; private final SimpleMetricsEventMulticaster eventMulticaster; private final MethodMetricsSampler methodSampler = new MethodMetricsSampler(this); private final ThreadPoolMetricsSampler threadPoolSampler = new ThreadPoolMetricsSampler(this); @@ -57,6 +55,9 @@ public class DefaultMetricsCollector implements MetricsCollector { samplers.add(threadPoolSampler); } + public void addSampler(MetricsSampler sampler){ + samplers.add(sampler); + } public void setApplicationName(String applicationName) { this.applicationName = applicationName; } @@ -81,6 +82,14 @@ public class DefaultMetricsCollector implements MetricsCollector { return collectEnabled; } + public boolean isThreadpoolCollectEnabled() { + return threadpoolCollectEnabled; + } + + public void setThreadpoolCollectEnabled(boolean threadpoolCollectEnabled) { + this.threadpoolCollectEnabled = threadpoolCollectEnabled; + } + public MethodMetricsSampler getMethodSampler() { return this.methodSampler; } @@ -95,7 +104,7 @@ public class DefaultMetricsCollector implements MetricsCollector { applicationSampler.inc(applicationName, MetricsEvent.Type.APPLICATION_INFO); } - public void registryDefaultSample(){ + public void registryDefaultSample() { this.threadPoolSampler.registryDefaultSampleThreadPoolExecutor(); } @@ -119,7 +128,9 @@ public class DefaultMetricsCollector implements MetricsCollector { List samples = new ArrayList<>(); this.getCount(MetricsEvent.Type.APPLICATION_INFO).filter(e -> !e.isEmpty()) .ifPresent(map -> map.forEach((k, v) -> - samples.add(new GaugeMetricSample<>(APPLICATION_METRIC_INFO, k.getTags(), APPLICATION, v, AtomicLong::get))) + samples.add(new CounterMetricSample<>(APPLICATION_METRIC_INFO.getName(), + APPLICATION_METRIC_INFO.getDescription(), + k.getTags(), APPLICATION, v))) ); return samples; } diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/HistogramMetricsCollector.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/HistogramMetricsCollector.java new file mode 100644 index 0000000000..28792e8b60 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/HistogramMetricsCollector.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.collector; + +import io.micrometer.core.instrument.Timer; +import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; +import org.apache.dubbo.config.MetricsConfig; +import org.apache.dubbo.config.context.ConfigManager; +import org.apache.dubbo.config.nested.HistogramConfig; +import org.apache.dubbo.metrics.MetricsGlobalRegistry; +import org.apache.dubbo.metrics.event.MetricsEvent; +import org.apache.dubbo.metrics.event.RTEvent; +import org.apache.dubbo.metrics.listener.MetricsListener; +import org.apache.dubbo.metrics.model.MethodMetric; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.register.HistogramMetricRegister; +import org.apache.dubbo.metrics.sample.HistogramMetricSample; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +import static org.apache.dubbo.metrics.model.MetricsCategory.RT; + +public class HistogramMetricsCollector implements MetricsListener { + + private final ConcurrentHashMap rt = new ConcurrentHashMap<>(); + private HistogramMetricRegister metricRegister; + private final ApplicationModel applicationModel; + + private static final Integer[] DEFAULT_BUCKETS_MS = new Integer[]{100, 300, 500, 1000, 3000, 5000, 10000}; + + public HistogramMetricsCollector(ApplicationModel applicationModel) { + this.applicationModel = applicationModel; + + ConfigManager configManager = applicationModel.getApplicationConfigManager(); + MetricsConfig config = configManager.getMetrics().orElse(null); + if (config != null && config.getHistogram() != null && Boolean.TRUE.equals(config.getHistogram().getEnabled())) { + registerListener(); + + HistogramConfig histogram = config.getHistogram(); + if (!Boolean.TRUE.equals(histogram.getEnabledPercentiles()) && histogram.getBucketsMs() == null) { + histogram.setBucketsMs(DEFAULT_BUCKETS_MS); + } + + metricRegister = new HistogramMetricRegister(MetricsGlobalRegistry.getCompositeRegistry(), histogram); + } + } + + private void registerListener() { + applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class).addListener(this); + } + + @Override + public void onEvent(MetricsEvent event) { + if (event instanceof RTEvent) { + onRTEvent((RTEvent) event); + } + } + + private void onRTEvent(RTEvent event) { + if (metricRegister != null) { + MethodMetric metric = (MethodMetric) event.getMetric(); + Long responseTime = event.getRt(); + + HistogramMetricSample sample = new HistogramMetricSample(MetricsKey.METRIC_RT_HISTOGRAM.getNameByType(metric.getSide()), + MetricsKey.METRIC_RT_HISTOGRAM.getDescription(), metric.getTags(), RT); + + Timer timer = ConcurrentHashMapUtils.computeIfAbsent(rt, metric, k -> metricRegister.register(sample)); + timer.record(responseTime, TimeUnit.MILLISECONDS); + } + } +} diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MethodMetricsSampler.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MethodMetricsSampler.java index a42f6c5b38..29a14e1ed5 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MethodMetricsSampler.java +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MethodMetricsSampler.java @@ -24,7 +24,8 @@ import org.apache.dubbo.metrics.event.RTEvent; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.Metric; import org.apache.dubbo.metrics.model.MetricsCategory; -import org.apache.dubbo.metrics.model.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.Invocation; @@ -36,8 +37,6 @@ import java.util.function.ToDoubleFunction; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; -import static org.apache.dubbo.metrics.model.MetricsCategory.REQUESTS; -import static org.apache.dubbo.metrics.model.MetricsCategory.RT; public class MethodMetricsSampler extends SimpleMetricsCountSampler { @@ -51,7 +50,7 @@ public class MethodMetricsSampler extends SimpleMetricsCountSampler sampleConfigure) { sampleConfigure.configureMetrics(configure -> new MethodMetric(collector.getApplicationName(), configure.getSource())); - sampleConfigure.configureEventHandler(configure -> collector.getEventMulticaster().publishEvent(new MethodEvent(configure.getMetric(), + sampleConfigure.configureEventHandler(configure -> collector.getEventMulticaster().publishEvent(new MethodEvent(collector.getApplicationModel(), configure.getMetric(), configure.getMetricName()))); } @@ -59,7 +58,7 @@ public class MethodMetricsSampler extends SimpleMetricsCountSampler sampleConfigure) { sampleConfigure.configureMetrics(configure -> new MethodMetric(collector.getApplicationName(), configure.getSource())); - sampleConfigure.configureEventHandler(configure -> collector.getEventMulticaster().publishEvent(new RTEvent(configure.getMetric(), configure.getRt()))); + sampleConfigure.configureEventHandler(configure -> collector.getEventMulticaster().publishEvent(new RTEvent(collector.getApplicationModel(), configure.getMetric(), configure.getRt()))); } @Override @@ -71,7 +70,7 @@ public class MethodMetricsSampler extends SimpleMetricsCountSampler>() { @Override public GaugeMetricSample newInstance(MetricsKey key, MethodMetric metric, T value, ToDoubleFunction apply) { - return getGaugeMetricSample(key, metric, RT, value, apply); + return createGaugeMetricSample(key, metric, MetricsCategory.RT, value, apply); } })); @@ -84,25 +83,25 @@ public class MethodMetricsSampler extends SimpleMetricsCountSampler list, String side) { - count(list, MetricsEvent.Type.TOTAL.getNameByType(side), MetricsKey.METRIC_REQUESTS); - count(list, MetricsEvent.Type.SUCCEED.getNameByType(side), MetricsKey.METRIC_REQUESTS_SUCCEED); - count(list, MetricsEvent.Type.UNKNOWN_FAILED.getNameByType(side), MetricsKey.METRIC_REQUESTS_FAILED); - count(list, MetricsEvent.Type.PROCESSING.getNameByType(side), MetricsKey.METRIC_REQUESTS_PROCESSING); - count(list, MetricsEvent.Type.BUSINESS_FAILED.getNameByType(side), MetricsKey.METRIC_REQUEST_BUSINESS_FAILED); - count(list, MetricsEvent.Type.REQUEST_TIMEOUT.getNameByType(side), MetricsKey.METRIC_REQUESTS_TIMEOUT); - count(list, MetricsEvent.Type.REQUEST_LIMIT.getNameByType(side), MetricsKey.METRIC_REQUESTS_LIMIT); - count(list, MetricsEvent.Type.TOTAL_FAILED.getNameByType(side), MetricsKey.METRIC_REQUESTS_TOTAL_FAILED); - count(list, MetricsEvent.Type.NETWORK_EXCEPTION.getNameByType(side), MetricsKey.METRIC_REQUESTS_NETWORK_FAILED); - count(list, MetricsEvent.Type.SERVICE_UNAVAILABLE.getNameByType(side), MetricsKey.METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED); - count(list, MetricsEvent.Type.CODEC_EXCEPTION.getNameByType(side), MetricsKey.METRIC_REQUESTS_CODEC_FAILED); + count(list, MetricsEvent.Type.TOTAL.getNameByType(side), MetricSample.Type.COUNTER, MetricsKey.METRIC_REQUESTS); + count(list, MetricsEvent.Type.SUCCEED.getNameByType(side), MetricSample.Type.COUNTER, MetricsKey.METRIC_REQUESTS_SUCCEED); + count(list, MetricsEvent.Type.UNKNOWN_FAILED.getNameByType(side), MetricSample.Type.COUNTER, MetricsKey.METRIC_REQUESTS_FAILED); + count(list, MetricsEvent.Type.PROCESSING.getNameByType(side), MetricSample.Type.GAUGE, MetricsKey.METRIC_REQUESTS_PROCESSING); + count(list, MetricsEvent.Type.BUSINESS_FAILED.getNameByType(side), MetricSample.Type.COUNTER, MetricsKey.METRIC_REQUEST_BUSINESS_FAILED); + count(list, MetricsEvent.Type.REQUEST_TIMEOUT.getNameByType(side), MetricSample.Type.COUNTER, MetricsKey.METRIC_REQUESTS_TIMEOUT); + count(list, MetricsEvent.Type.REQUEST_LIMIT.getNameByType(side), MetricSample.Type.COUNTER, MetricsKey.METRIC_REQUESTS_LIMIT); + count(list, MetricsEvent.Type.TOTAL_FAILED.getNameByType(side), MetricSample.Type.COUNTER, MetricsKey.METRIC_REQUESTS_TOTAL_FAILED); + count(list, MetricsEvent.Type.NETWORK_EXCEPTION.getNameByType(side), MetricSample.Type.COUNTER, MetricsKey.METRIC_REQUESTS_NETWORK_FAILED); + count(list, MetricsEvent.Type.SERVICE_UNAVAILABLE.getNameByType(side), MetricSample.Type.COUNTER, MetricsKey.METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED); + count(list, MetricsEvent.Type.CODEC_EXCEPTION.getNameByType(side), MetricSample.Type.COUNTER, MetricsKey.METRIC_REQUESTS_CODEC_FAILED); } - private GaugeMetricSample getGaugeMetricSample(MetricsKey metricsKey, - MethodMetric methodMetric, - MetricsCategory metricsCategory, - T value, - ToDoubleFunction apply) { + private GaugeMetricSample createGaugeMetricSample(MetricsKey metricsKey, + MethodMetric methodMetric, + MetricsCategory metricsCategory, + T value, + ToDoubleFunction apply) { return new GaugeMetricSample<>( metricsKey.getNameByType(methodMetric.getSide()), metricsKey.getDescription(), @@ -112,9 +111,23 @@ public class MethodMetricsSampler extends SimpleMetricsCountSampler void count(List list, String eventType, MetricsKey metricsKey) { + private void count(List list, String eventType, MetricSample.Type type, MetricsKey metricsKey) { getCount(eventType).filter(e -> !e.isEmpty()) - .ifPresent(map -> map.forEach((k, v) -> - list.add(getGaugeMetricSample(metricsKey, k, REQUESTS, v, AtomicLong::get)))); + .ifPresent(map -> map.forEach((k, v) -> { + if(type == MetricSample.Type.COUNTER){ + list.add(createCounterMetricSample(metricsKey, k, MetricsCategory.REQUESTS, v)); + }else if(type == MetricSample.Type.GAUGE){ + list.add(createGaugeMetricSample(metricsKey, k, MetricsCategory.REQUESTS, v, AtomicLong::get)); + } + } + )); + } + + private MetricSample createCounterMetricSample(MetricsKey metricsKey, MethodMetric methodMetric, MetricsCategory metricsCategory, AtomicLong value) { + return new CounterMetricSample<>(metricsKey.getNameByType(methodMetric.getSide()), + metricsKey.getDescription(), + methodMetric.getTags(), metricsCategory, value); + + } } diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MetricThreadPoolExhaustedListener.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MetricThreadPoolExhaustedListener.java new file mode 100644 index 0000000000..2a77b70542 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MetricThreadPoolExhaustedListener.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.collector.sample; +import org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedEvent; +import org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedListener; +import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; + +public class MetricThreadPoolExhaustedListener implements ThreadPoolExhaustedListener { + + private final ThreadRejectMetricsCountSampler threadRejectMetricsCountSampler; + + private final String threadPoolExecutorName; + + public MetricThreadPoolExhaustedListener(String threadPoolExecutorName,DefaultMetricsCollector collector) { + this.threadPoolExecutorName=threadPoolExecutorName; + this.threadRejectMetricsCountSampler = new ThreadRejectMetricsCountSampler(collector); + } + + public MetricThreadPoolExhaustedListener(String threadPoolExecutorName,ThreadRejectMetricsCountSampler sampler) { + this.threadPoolExecutorName=threadPoolExecutorName; + this.threadRejectMetricsCountSampler=sampler; + } + @Override + public void onEvent(ThreadPoolExhaustedEvent event) { + threadRejectMetricsCountSampler.addMetricName(threadPoolExecutorName); + threadRejectMetricsCountSampler.incOnEvent(threadPoolExecutorName,threadPoolExecutorName); + } +} diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MetricsCountSampler.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MetricsCountSampler.java index 353a04fe99..8bcd5a976b 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MetricsCountSampler.java +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MetricsCountSampler.java @@ -18,7 +18,7 @@ package org.apache.dubbo.metrics.collector.sample; import org.apache.dubbo.metrics.model.Metric; -import org.apache.dubbo.metrics.model.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.List; diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/SimpleMetricsCountSampler.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/SimpleMetricsCountSampler.java index 6fd84204e0..167fbb383b 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/SimpleMetricsCountSampler.java +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/SimpleMetricsCountSampler.java @@ -20,7 +20,7 @@ package org.apache.dubbo.metrics.collector.sample; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.metrics.model.Metric; -import org.apache.dubbo.metrics.model.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.ArrayList; diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/ThreadPoolMetricsSampler.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/ThreadPoolMetricsSampler.java index adc4118d4f..ec4a74b98d 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/ThreadPoolMetricsSampler.java +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/ThreadPoolMetricsSampler.java @@ -20,9 +20,10 @@ import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.store.DataStore; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; +import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; -import org.apache.dubbo.metrics.model.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.ThreadPoolMetric; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; @@ -122,10 +123,18 @@ public class ThreadPoolMetricsSampler implements MetricsSampler { this.addExecutors(CLIENT_THREAD_POOL_NAME + "-" + entry.getKey(), executor); } } + + ThreadRejectMetricsCountSampler threadRejectMetricsCountSampler = new ThreadRejectMetricsCountSampler(collector); + this.sampleThreadPoolExecutor.entrySet().stream().filter(entry->entry.getKey().startsWith(SERVER_THREAD_POOL_NAME)).forEach(entry->{ + if(entry.getValue().getRejectedExecutionHandler() instanceof AbortPolicyWithReport) { + MetricThreadPoolExhaustedListener metricThreadPoolExhaustedListener=new MetricThreadPoolExhaustedListener(entry.getKey(),threadRejectMetricsCountSampler); + ((AbortPolicyWithReport) entry.getValue().getRejectedExecutionHandler()).addThreadPoolExhaustedEventListener(metricThreadPoolExhaustedListener); + } + }); } if (this.frameworkExecutorRepository != null) { this.addExecutors("sharedExecutor", frameworkExecutorRepository.getSharedExecutor()); - } + } } } diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/ThreadRejectMetricsCountSampler.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/ThreadRejectMetricsCountSampler.java new file mode 100644 index 0000000000..e9e55f9448 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/ThreadRejectMetricsCountSampler.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.dubbo.metrics.collector.sample; +import org.apache.dubbo.common.utils.ConcurrentHashSet; +import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; +import org.apache.dubbo.metrics.model.Metric; +import org.apache.dubbo.metrics.model.MetricsCategory; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.ThreadPoolRejectMetric; +import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; +import org.apache.dubbo.metrics.model.sample.MetricSample; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.ToDoubleFunction; +import static org.apache.dubbo.metrics.model.MetricsCategory.THREAD_POOL; + +public class ThreadRejectMetricsCountSampler extends SimpleMetricsCountSampler { + + private final DefaultMetricsCollector collector; + + private final Set metricNames = new ConcurrentHashSet<>(); + public ThreadRejectMetricsCountSampler(DefaultMetricsCollector collector) { + this.collector = collector; + this.collector.addSampler(this); + } + + public void addMetricName(String name){ + this.metricNames.add(name); + } + + @Override + public List sample() { + List metricSamples = new ArrayList<>(); + metricNames.stream().forEach(name->collect(metricSamples,name)); + return metricSamples; + } + + + private void collect(List list, String metricName) { + count(list, metricName, MetricsKey.THREAD_POOL_THREAD_REJECT_COUNT); + } + + private void count(List list, String metricName, MetricsKey metricsKey) { + getCount(metricName).filter(e -> !e.isEmpty()) + .ifPresent(map -> map.forEach((k, v) -> + list.add(getGaugeMetricSample(metricsKey, k, THREAD_POOL, v, AtomicLong::get)))); + } + + private GaugeMetricSample getGaugeMetricSample(MetricsKey metricsKey, + ThreadPoolRejectMetric methodMetric, + MetricsCategory metricsCategory, + T value, + ToDoubleFunction apply) { + return new GaugeMetricSample<>( + metricsKey.getNameByType(methodMetric.getThreadPoolName()), + metricsKey.getDescription(), + methodMetric.getTags(), + metricsCategory, + value, + apply); + } + + @Override + protected void countConfigure(MetricsCountSampleConfigurer sampleConfigure) { + sampleConfigure.configureMetrics(configure -> new ThreadPoolRejectMetric(collector.getApplicationName(),configure.getSource())); + } +} diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/filter/MethodMetricsInterceptor.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/filter/MethodMetricsInterceptor.java index 74fd349f7b..b4767f3f77 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/filter/MethodMetricsInterceptor.java +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/filter/MethodMetricsInterceptor.java @@ -86,17 +86,11 @@ public class MethodMetricsInterceptor { } sampler.incOnEvent(invocation, eventType.getNameByType(side)); } - - if (throwable instanceof RpcException && ((RpcException) throwable).isBiz()) { - onCompleted(invocation); - }else{ - rtTime(invocation); - } - + onCompleted(invocation); sampler.incOnEvent(invocation, MetricsEvent.Type.TOTAL_FAILED.getNameByType(side)); } - private void rtTime(Invocation invocation){ + private void rtTime(Invocation invocation) { Long endTime = System.currentTimeMillis(); Long beginTime = (Long) invocation.get(METRIC_FILTER_START_TIME); Long rt = endTime - beginTime; diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/observation/ObservationReceiverFilter.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/observation/ObservationReceiverFilter.java index a25cc92ed8..5a33ced437 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/observation/ObservationReceiverFilter.java +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/observation/ObservationReceiverFilter.java @@ -16,9 +16,6 @@ */ package org.apache.dubbo.metrics.observation; -import io.micrometer.observation.Observation; -import io.micrometer.observation.ObservationRegistry; - import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.BaseFilter; import org.apache.dubbo.rpc.Filter; @@ -29,6 +26,9 @@ import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationRegistry; + import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; /** @@ -37,13 +37,17 @@ import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; @Activate(group = PROVIDER, order = -1, onClass = "io.micrometer.observation.NoopObservationRegistry") public class ObservationReceiverFilter implements Filter, BaseFilter.Listener, ScopeModelAware { - private final ObservationRegistry observationRegistry; + private ObservationRegistry observationRegistry; - private final DubboServerObservationConvention serverObservationConvention; + private DubboServerObservationConvention serverObservationConvention; public ObservationReceiverFilter(ApplicationModel applicationModel) { - observationRegistry = applicationModel.getBeanFactory().getBean(ObservationRegistry.class); - serverObservationConvention = applicationModel.getBeanFactory().getBean(DubboServerObservationConvention.class); + applicationModel.getApplicationConfigManager().getTracing().ifPresent(cfg -> { + if (Boolean.TRUE.equals(cfg.getEnabled())) { + observationRegistry = applicationModel.getBeanFactory().getBean(ObservationRegistry.class); + serverObservationConvention = applicationModel.getBeanFactory().getBean(DubboServerObservationConvention.class); + } + }); } @Override @@ -51,15 +55,18 @@ public class ObservationReceiverFilter implements Filter, BaseFilter.Listener, S if (observationRegistry == null) { return invoker.invoke(invocation); } - DubboServerContext receiverContext = new DubboServerContext(invoker, invocation); - Observation observation = DubboObservation.SERVER.observation(this.serverObservationConvention, DefaultDubboServerObservationConvention.INSTANCE, () -> receiverContext, observationRegistry); + final DubboServerContext receiverContext = new DubboServerContext(invoker, invocation); + final Observation observation = DubboObservationDocumentation.SERVER.observation( + this.serverObservationConvention, + DefaultDubboServerObservationConvention.getInstance(), + () -> receiverContext, observationRegistry); invocation.put(Observation.class, observation.start()); return observation.scoped(() -> invoker.invoke(invocation)); } @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { - Observation observation = (Observation) invocation.get(Observation.class); + final Observation observation = getObservation(invocation); if (observation == null) { return; } @@ -68,11 +75,15 @@ public class ObservationReceiverFilter implements Filter, BaseFilter.Listener, S @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { - Observation observation = (Observation) invocation.get(Observation.class); + final Observation observation = getObservation(invocation); if (observation == null) { return; } observation.error(t); observation.stop(); } + + private Observation getObservation(Invocation invocation) { + return (Observation) invocation.get(Observation.class); + } } diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/register/HistogramMetricRegister.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/register/HistogramMetricRegister.java new file mode 100644 index 0000000000..04a173cdee --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/register/HistogramMetricRegister.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.register; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Timer; +import org.apache.dubbo.config.nested.HistogramConfig; +import org.apache.dubbo.metrics.sample.HistogramMetricSample; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HistogramMetricRegister implements MetricRegister { + + private final MeterRegistry registry; + private final HistogramConfig config; + + public HistogramMetricRegister(MeterRegistry registry, HistogramConfig config) { + this.registry = registry; + this.config = config; + } + + @Override + public Timer register(HistogramMetricSample sample) { + List tags = new ArrayList<>(); + sample.getTags().forEach((k, v) -> { + if (v == null) { + v = ""; + } + + tags.add(Tag.of(k, v)); + }); + + Timer.Builder builder = Timer.builder(sample.getName()).description(sample.getDescription()).tags(tags); + + if (Boolean.TRUE.equals(config.getEnabledPercentiles())) { + builder.publishPercentileHistogram(true); + } + + if (config.getPercentiles() != null) { + builder.publishPercentiles(config.getPercentiles()); + } + + if (config.getBucketsMs() != null) { + builder.serviceLevelObjectives(Arrays.stream(config.getBucketsMs()) + .map(Duration::ofMillis).toArray(Duration[]::new)); + } + + if (config.getMinExpectedMs() != null) { + builder.minimumExpectedValue(Duration.ofMillis(config.getMinExpectedMs())); + } + + if (config.getMaxExpectedMs() != null) { + builder.maximumExpectedValue(Duration.ofMillis(config.getMaxExpectedMs())); + } + + if (config.getDistributionStatisticExpiryMin() != null) { + builder.distributionStatisticExpiry(Duration.ofMinutes(config.getDistributionStatisticExpiryMin())); + } + + return builder.register(registry); + } +} diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/register/MetricRegister.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/register/MetricRegister.java new file mode 100644 index 0000000000..fbf6c5eeb0 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/register/MetricRegister.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.register; + +import io.micrometer.core.instrument.Meter; +import org.apache.dubbo.metrics.model.sample.MetricSample; + +public interface MetricRegister { + + M register(S sample); + +} diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/AbstractMetricsReporter.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/AbstractMetricsReporter.java index 4a0558b387..5f93e8cda2 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/AbstractMetricsReporter.java +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/AbstractMetricsReporter.java @@ -17,6 +17,7 @@ package org.apache.dubbo.metrics.report; +import io.micrometer.core.instrument.FunctionCounter; import io.micrometer.core.instrument.binder.MeterBinder; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; @@ -24,9 +25,12 @@ import org.apache.dubbo.common.constants.MetricsConstants; import org.apache.dubbo.common.lang.ShutdownHookCallbacks; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.metrics.MetricsGlobalRegistry; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.metrics.collector.AggregateMetricsCollector; import org.apache.dubbo.metrics.collector.MetricsCollector; +import org.apache.dubbo.metrics.collector.HistogramMetricsCollector; +import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; @@ -68,7 +72,7 @@ public abstract class AbstractMetricsReporter implements MetricsReporter { protected final List collectors = new ArrayList<>(); // Avoid instances being gc due to weak references protected final List instanceHolder = new ArrayList<>(); - public static final CompositeMeterRegistry compositeRegistry = new CompositeMeterRegistry(); + protected final CompositeMeterRegistry compositeRegistry; private final ApplicationModel applicationModel; @@ -80,6 +84,7 @@ public abstract class AbstractMetricsReporter implements MetricsReporter { protected AbstractMetricsReporter(URL url, ApplicationModel applicationModel) { this.url = url; this.applicationModel = applicationModel; + this.compositeRegistry = MetricsGlobalRegistry.getCompositeRegistry(); } @Override @@ -134,6 +139,7 @@ public abstract class AbstractMetricsReporter implements MetricsReporter { private void initCollectors() { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); beanFactory.getOrRegisterBean(AggregateMetricsCollector.class); + beanFactory.getOrRegisterBean(HistogramMetricsCollector.class); List otherCollectors = beanFactory.getBeansOfType(MetricsCollector.class); collectors.addAll(otherCollectors); } @@ -153,19 +159,17 @@ public abstract class AbstractMetricsReporter implements MetricsReporter { switch (sample.getType()) { case GAUGE: GaugeMetricSample gaugeSample = (GaugeMetricSample) sample; - List tags = new ArrayList<>(); - gaugeSample.getTags().forEach((k, v) -> { - if (v == null) { - v = ""; - } - - tags.add(Tag.of(k, v)); - }); + List tags = getTags(gaugeSample); Gauge.builder(gaugeSample.getName(), gaugeSample.getValue(), gaugeSample.getApply()) .description(gaugeSample.getDescription()).tags(tags).register(compositeRegistry); break; case COUNTER: + CounterMetricSample counterMetricSample = (CounterMetricSample) sample; + FunctionCounter.builder(counterMetricSample.getName(), counterMetricSample.getValue(), + Number::doubleValue).description(counterMetricSample.getDescription()) + .tags(getTags(counterMetricSample)) + .register(compositeRegistry); case TIMER: case LONG_TASK_TIMER: case DISTRIBUTION_SUMMARY: @@ -181,6 +185,18 @@ public abstract class AbstractMetricsReporter implements MetricsReporter { }); } + private static List getTags(MetricSample gaugeSample) { + List tags = new ArrayList<>(); + gaugeSample.getTags().forEach((k, v) -> { + if (v == null) { + v = ""; + } + + tags.add(Tag.of(k, v)); + }); + return tags; + } + private void registerDubboShutdownHook() { applicationModel.getBeanFactory().getBean(ShutdownHookCallbacks.class).addCallback(this::destroy); } diff --git a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/event/RequestEventTest.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/sample/HistogramMetricSample.java similarity index 56% rename from dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/event/RequestEventTest.java rename to dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/sample/HistogramMetricSample.java index d4031e90d5..8f8149c1b5 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/event/RequestEventTest.java +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/sample/HistogramMetricSample.java @@ -15,23 +15,20 @@ * limitations under the License. */ -package org.apache.dubbo.metrics.metrics.event; +package org.apache.dubbo.metrics.sample; -import org.apache.dubbo.metrics.event.MetricsEvent; -import org.apache.dubbo.metrics.event.RequestEvent; -import org.apache.dubbo.metrics.model.MethodMetric; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; +import org.apache.dubbo.metrics.model.MetricsCategory; +import org.apache.dubbo.metrics.model.sample.MetricSample; -class RequestEventTest { +import java.util.Map; - @Test - void testNewEvent() { - MethodMetric metric = new MethodMetric(); - MetricsEvent.Type type = MetricsEvent.Type.TOTAL; - RequestEvent event = new RequestEvent(metric, type); +public class HistogramMetricSample extends MetricSample { - Assertions.assertEquals(event.getSource(), metric); - Assertions.assertEquals(event.getType(), type); + public HistogramMetricSample(String name, String description, Map tags, MetricsCategory category) { + super(name, description, tags, Type.TIMER, category); + } + + public HistogramMetricSample(String name, String description, Map tags, Type type, MetricsCategory category, String baseUnit) { + super(name, description, tags, type, category, baseUnit); } } diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter b/dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter deleted file mode 100644 index 5714fefb30..0000000000 --- a/dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter +++ /dev/null @@ -1,2 +0,0 @@ -observationsender=org.apache.dubbo.metrics.observation.ObservationSenderFilter -metricsClusterFilter=org.apache.dubbo.metrics.filter.MetricsClusterFilter diff --git a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollectorTest.java b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollectorTest.java index 6514aa0d9c..f2275125b1 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollectorTest.java +++ b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollectorTest.java @@ -25,7 +25,7 @@ import org.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.metrics.TestMetricsInvoker; import org.apache.dubbo.metrics.collector.sample.MethodMetricsSampler; import org.apache.dubbo.metrics.event.MetricsEvent; -import org.apache.dubbo.metrics.model.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.RpcContext; diff --git a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/collector/ConfigCenterMetricsCollectorTest.java b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/collector/ConfigCenterMetricsCollectorTest.java new file mode 100644 index 0000000000..e234279339 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/collector/ConfigCenterMetricsCollectorTest.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.collector; + +import org.apache.dubbo.common.config.configcenter.ConfigChangeType; +import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.metrics.model.ConfigCenterMetric; +import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; +import org.apache.dubbo.metrics.model.sample.MetricSample; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.FrameworkModel; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_METHOD_KEY; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_VERSION_KEY; +import static org.junit.jupiter.api.Assertions.*; + +class ConfigCenterMetricsCollectorTest { + + private FrameworkModel frameworkModel; + private ApplicationModel applicationModel; + + @BeforeEach + public void setup() { + frameworkModel = FrameworkModel.defaultModel(); + applicationModel = frameworkModel.newApplication(); + ApplicationConfig config = new ApplicationConfig(); + config.setName("MockMetrics"); + + applicationModel.getApplicationConfigManager().setApplication(config); + } + + @AfterEach + public void teardown() { + applicationModel.destroy(); + } + + @Test + void increase4Initialized() { + ConfigCenterMetricsCollector collector = new ConfigCenterMetricsCollector(applicationModel); + collector.setCollectEnabled(true); + String applicationName = applicationModel.getApplicationName(); + collector.increase4Initialized("key", "group", "nacos", applicationName, 1); + collector.increase4Initialized("key", "group", "nacos", applicationName, 1); + + List samples = collector.collect(); + for (MetricSample sample : samples) { + Assertions.assertTrue(sample instanceof GaugeMetricSample); + GaugeMetricSample gaugeSample = (GaugeMetricSample) sample; + Map tags = gaugeSample.getTags(); + + Assertions.assertEquals(gaugeSample.applyAsLong(), 2); + Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationName); + } + } + + @Test + void increaseUpdated() { + ConfigCenterMetricsCollector collector = new ConfigCenterMetricsCollector(applicationModel); + collector.setCollectEnabled(true); + String applicationName = applicationModel.getApplicationName(); + + ConfigChangedEvent event = new ConfigChangedEvent("key", "group", null, ConfigChangeType.ADDED); + + collector.increaseUpdated("nacos", applicationName, event); + collector.increaseUpdated("nacos", applicationName, event); + + List samples = collector.collect(); + for (MetricSample sample : samples) { + Assertions.assertTrue(sample instanceof GaugeMetricSample); + GaugeMetricSample gaugeSample = (GaugeMetricSample) sample; + Map tags = gaugeSample.getTags(); + + Assertions.assertEquals(gaugeSample.applyAsLong(), 2); + Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationName); + } + } +} diff --git a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/filter/MetricsFilterTest.java b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/filter/MetricsFilterTest.java index a50fd29dc6..e8d0cb8b9f 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/filter/MetricsFilterTest.java +++ b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/filter/MetricsFilterTest.java @@ -22,8 +22,8 @@ import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metrics.TestMetricsInvoker; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; -import org.apache.dubbo.metrics.model.MetricsKey; -import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invoker; @@ -32,7 +32,6 @@ import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -45,18 +44,9 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import java.util.stream.Collectors; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_PARAMETER_DESC; -import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY; -import static org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY; -import static org.apache.dubbo.common.constants.MetricsConstants.TAG_METHOD_KEY; -import static org.apache.dubbo.common.constants.MetricsConstants.TAG_VERSION_KEY; +import static org.apache.dubbo.common.constants.MetricsConstants.*; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -64,7 +54,6 @@ class MetricsFilterTest { private ApplicationModel applicationModel; private MetricsFilter filter; - private MetricsClusterFilter metricsClusterFilter; private DefaultMetricsCollector collector; private RpcInvocation invocation; private final Invoker invoker = mock(Invoker.class); @@ -91,7 +80,7 @@ class MetricsFilterTest { filter = new MetricsFilter(); collector = applicationModel.getBeanFactory().getOrRegisterBean(DefaultMetricsCollector.class); - if (!initApplication.get()) { + if(!initApplication.get()) { collector.collectApplication(applicationModel); initApplication.set(true); } @@ -99,9 +88,6 @@ class MetricsFilterTest { side = CommonConstants.CONSUMER; invocation.setInvoker(new TestMetricsInvoker(side)); RpcContext.getServiceContext().setUrl(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&side=" + side)); - - metricsClusterFilter = new MetricsClusterFilter(); - metricsClusterFilter.setApplicationModel(applicationModel); } @AfterEach @@ -196,10 +182,10 @@ class MetricsFilterTest { Assertions.assertTrue(metricsMap.containsKey(MetricsKey.METRIC_REQUESTS_TOTAL_FAILED.getNameByType(side))); MetricSample timeoutSample = metricsMap.get(MetricsKey.METRIC_REQUESTS_TIMEOUT.getNameByType(side)); - Assertions.assertSame(((GaugeMetricSample) timeoutSample).applyAsLong(), count); + Assertions.assertSame(((CounterMetricSample) timeoutSample).getValue().longValue(), count); - GaugeMetricSample failedSample = (GaugeMetricSample) metricsMap.get(MetricsKey.METRIC_REQUESTS_TOTAL_FAILED.getNameByType(side)); - Assertions.assertSame(failedSample.applyAsLong(), count); + CounterMetricSample failedSample = (CounterMetricSample) metricsMap.get(MetricsKey.METRIC_REQUESTS_TOTAL_FAILED.getNameByType(side)); + Assertions.assertSame(failedSample.getValue().longValue(), count); } @Test @@ -224,7 +210,7 @@ class MetricsFilterTest { MetricSample sample = metricsMap.get(MetricsKey.METRIC_REQUESTS_LIMIT.getNameByType(side)); - Assertions.assertSame(((GaugeMetricSample) sample).applyAsLong(), count); + Assertions.assertSame(((CounterMetricSample) sample).getValue().longValue(), count); } @Test @@ -274,45 +260,14 @@ class MetricsFilterTest { } @Test - public void testErrors() { - testFilterError(RpcException.SERIALIZATION_EXCEPTION, - MetricsKey.METRIC_REQUESTS_CODEC_FAILED.getNameByType(side)); - testFilterError(RpcException.NETWORK_EXCEPTION, - MetricsKey.METRIC_REQUESTS_NETWORK_FAILED.getNameByType(side)); + public void testErrors(){ + testFilterError(RpcException.SERIALIZATION_EXCEPTION, MetricsKey.METRIC_REQUESTS_CODEC_FAILED.formatName(side)); + testFilterError(RpcException.NETWORK_EXCEPTION, MetricsKey.METRIC_REQUESTS_NETWORK_FAILED.formatName(side)); } - @Test - public void testNoProvider() { - testClusterFilterError(RpcException.FORBIDDEN_EXCEPTION, - MetricsKey.METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED.getNameByType(CommonConstants.CONSUMER)); - } - private void testClusterFilterError(int errorCode, String name) { -// setup(); - collector.setCollectEnabled(true); - given(invoker.invoke(invocation)).willThrow(new RpcException(errorCode)); - initParam(); - Long count = 1L; - - for (int i = 0; i < count; i++) { - try { - metricsClusterFilter.invoke(invoker, invocation); - } catch (Exception e) { - Assertions.assertTrue(e instanceof RpcException); - metricsClusterFilter.onError(e, invoker, invocation); - } - } - Map metricsMap = getMetricsMap(); - Assertions.assertTrue(metricsMap.containsKey(name)); - - MetricSample sample = metricsMap.get(name); - - Assertions.assertSame(((GaugeMetricSample) sample).applyAsLong(), count); - teardown(); - } - - private void testFilterError(int errorCode, String name) { + private void testFilterError(int errorCode,MetricsKey metricsKey){ setup(); collector.setCollectEnabled(true); given(invoker.invoke(invocation)).willThrow(new RpcException(errorCode)); @@ -329,14 +284,14 @@ class MetricsFilterTest { } } Map metricsMap = getMetricsMap(); - Assertions.assertTrue(metricsMap.containsKey(name)); + Assertions.assertTrue(metricsMap.containsKey(metricsKey.getName())); - MetricSample sample = metricsMap.get(name); + MetricSample sample = metricsMap.get(metricsKey.getName()); - Assertions.assertSame(((GaugeMetricSample) sample).applyAsLong(), count); + Assertions.assertSame(((CounterMetricSample) sample).getValue().longValue(), count); - Assertions.assertTrue(metricsMap.containsKey(name)); + Assertions.assertTrue(metricsMap.containsKey(metricsKey.getName())); Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME); diff --git a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/collector/DefaultMetricsCollectorTest.java b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/collector/DefaultMetricsCollectorTest.java index 7affb9ae8b..d492c21e9e 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/collector/DefaultMetricsCollectorTest.java +++ b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/collector/DefaultMetricsCollectorTest.java @@ -27,7 +27,8 @@ import org.apache.dubbo.metrics.event.MethodEvent; import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.event.RTEvent; import org.apache.dubbo.metrics.listener.MetricsListener; -import org.apache.dubbo.metrics.model.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.RpcContext; @@ -107,21 +108,34 @@ class DefaultMetricsCollectorTest { List samples = collector.collect(); for (MetricSample sample : samples) { - Assertions.assertTrue(sample instanceof GaugeMetricSample); - GaugeMetricSample gaugeSample = (GaugeMetricSample) sample; - Map tags = gaugeSample.getTags(); + if(sample instanceof GaugeMetricSample) { + GaugeMetricSample gaugeSample = (GaugeMetricSample) sample; + Assertions.assertEquals(gaugeSample.applyAsLong(), 1); + }else if(sample instanceof CounterMetricSample){ + CounterMetricSample counterMetricSample = (CounterMetricSample) sample; + Assertions.assertEquals(counterMetricSample.getValue().longValue(), 1); + } + Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), interfaceName); Assertions.assertEquals(tags.get(TAG_METHOD_KEY), methodName); Assertions.assertEquals(tags.get(TAG_GROUP_KEY), group); Assertions.assertEquals(tags.get(TAG_VERSION_KEY), version); - Assertions.assertEquals(gaugeSample.applyAsLong(), 1); + } methodMetricsCountSampler.dec(invocation, MetricsEvent.Type.PROCESSING.getNameByType(side)); samples = collector.collect(); - Map sampleMap = samples.stream().collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); + Map sampleMap = samples.stream().collect(Collectors.toMap(MetricSample::getName, k -> { + if(k instanceof GaugeMetricSample){ + return ((GaugeMetricSample) k).applyAsLong(); + }else if(k instanceof CounterMetricSample){ + return ((CounterMetricSample)k).getValue().longValue(); + }else{ + throw new RuntimeException("un support sample type"); + } + })); Assertions.assertEquals(sampleMap.get(MetricsKey.METRIC_REQUESTS_PROCESSING.getNameByType(side)), 0L); } diff --git a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/event/RTEventTest.java b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/event/RTEventTest.java index e5134fc12e..a69c166318 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/event/RTEventTest.java +++ b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/event/RTEventTest.java @@ -19,6 +19,7 @@ package org.apache.dubbo.metrics.metrics.event; import org.apache.dubbo.metrics.event.RTEvent; import org.apache.dubbo.metrics.model.MethodMetric; +import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -28,9 +29,9 @@ class RTEventTest { void testNewEvent() { MethodMetric metric = new MethodMetric(); Long rt = 5L; - RTEvent event = new RTEvent(metric, rt); + RTEvent event = new RTEvent(ApplicationModel.defaultModel(), metric, rt); - Assertions.assertEquals(event.getSource(), metric); + Assertions.assertEquals(event.getSource(), ApplicationModel.defaultModel()); Assertions.assertEquals(event.getRt(), rt); } } diff --git a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/observation/AbstractObservationFilterTest.java b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/observation/AbstractObservationFilterTest.java index db653e865a..99bf615947 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/observation/AbstractObservationFilterTest.java +++ b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/observation/AbstractObservationFilterTest.java @@ -19,6 +19,7 @@ package org.apache.dubbo.metrics.observation; import io.micrometer.tracing.test.SampleTestRunner; import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.BaseFilter; import org.apache.dubbo.rpc.Invoker; @@ -63,6 +64,9 @@ abstract class AbstractObservationFilterTest extends SampleTestRunner { invocation.addInvokedInvoker(invoker); applicationModel.getBeanFactory().registerBean(getObservationRegistry()); + TracingConfig tracingConfig = new TracingConfig(); + tracingConfig.setEnabled(true); + applicationModel.getApplicationConfigManager().setTracing(tracingConfig); filter = createFilter(applicationModel); diff --git a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/sampler/CountSamplerTest.java b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/sampler/CountSamplerTest.java index ed9cbca73b..b56d194c76 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/sampler/CountSamplerTest.java +++ b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/sampler/CountSamplerTest.java @@ -21,7 +21,7 @@ import org.apache.dubbo.metrics.collector.sample.MetricsCountSampleConfigurer; import org.apache.dubbo.metrics.collector.sample.MetricsCountSampler; import org.apache.dubbo.metrics.collector.sample.SimpleMetricsCountSampler; import org.apache.dubbo.metrics.model.Metric; -import org.apache.dubbo.metrics.model.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/MetadataMetricsConstants.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/MetadataMetricsConstants.java new file mode 100644 index 0000000000..8f095d58fc --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/MetadataMetricsConstants.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.metadata; + +import org.apache.dubbo.metrics.metadata.type.ApplicationType; +import org.apache.dubbo.metrics.metadata.type.ServiceType; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsLevel; +import org.apache.dubbo.metrics.model.key.MetricsPlaceType; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public interface MetadataMetricsConstants { + + MetricsPlaceType OP_TYPE_PUSH = MetricsPlaceType.of("push", MetricsLevel.APP); + MetricsPlaceType OP_TYPE_SUBSCRIBE = MetricsPlaceType.of("subscribe", MetricsLevel.APP); + MetricsPlaceType OP_TYPE_STORE_PROVIDER_INTERFACE = MetricsPlaceType.of("store.provider.interface", MetricsLevel.SERVICE); + + // App-level + List appKeys = Arrays.stream(ApplicationType.values()).map(ApplicationType::getMetricsKey).collect(Collectors.toList()); + + // Service-level + List serviceKeys = Arrays.stream(ServiceType.values()).map(ServiceType::getMetricsKey).collect(Collectors.toList()); +} diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/collector/MetadataMetricsCollector.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/collector/MetadataMetricsCollector.java index 405abe7390..81d85024f6 100644 --- a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/collector/MetadataMetricsCollector.java +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/collector/MetadataMetricsCollector.java @@ -21,11 +21,18 @@ import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.metrics.collector.ApplicationMetricsCollector; import org.apache.dubbo.metrics.collector.MetricsCollector; +import org.apache.dubbo.metrics.data.ApplicationStatComposite; +import org.apache.dubbo.metrics.data.BaseStatComposite; +import org.apache.dubbo.metrics.data.RtStatComposite; +import org.apache.dubbo.metrics.data.ServiceStatComposite; import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.event.MetricsEventMulticaster; -import org.apache.dubbo.metrics.metadata.collector.stat.MetadataStatComposite; +import org.apache.dubbo.metrics.metadata.MetadataMetricsConstants; import org.apache.dubbo.metrics.metadata.event.MetadataEvent; import org.apache.dubbo.metrics.metadata.event.MetadataMetricsEventMulticaster; +import org.apache.dubbo.metrics.metadata.type.ApplicationType; +import org.apache.dubbo.metrics.metadata.type.ServiceType; +import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; @@ -33,20 +40,31 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_PUSH; +import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_STORE_PROVIDER_INTERFACE; +import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_SUBSCRIBE; + /** * Registry implementation of {@link MetricsCollector} */ @Activate -public class MetadataMetricsCollector implements ApplicationMetricsCollector { +public class MetadataMetricsCollector implements ApplicationMetricsCollector { private Boolean collectEnabled = null; - private final MetadataStatComposite stats; + private final BaseStatComposite stats; private final MetricsEventMulticaster metadataEventMulticaster; private final ApplicationModel applicationModel; public MetadataMetricsCollector(ApplicationModel applicationModel) { - this.stats = new MetadataStatComposite(); + this.stats = new BaseStatComposite() { + @Override + protected void init(ApplicationStatComposite applicationStatComposite, ServiceStatComposite serviceStatComposite, RtStatComposite rtStatComposite) { + applicationStatComposite.init(MetadataMetricsConstants.appKeys); + serviceStatComposite.init(MetadataMetricsConstants.serviceKeys); + rtStatComposite.init(OP_TYPE_PUSH, OP_TYPE_SUBSCRIBE, OP_TYPE_STORE_PROVIDER_INTERFACE); + } + }; this.metadataEventMulticaster = new MetadataMetricsEventMulticaster(); this.applicationModel = applicationModel; } @@ -61,19 +79,27 @@ public class MetadataMetricsCollector implements ApplicationMetricsCollector setCollectEnabled(metricsConfig.getEnableMetadataMetrics())); + configManager.getMetrics().ifPresent(metricsConfig -> setCollectEnabled(metricsConfig.getEnableMetadata())); } - return Optional.ofNullable(collectEnabled).orElse(false); + return Optional.ofNullable(collectEnabled).orElse(true); } @Override - public void increment(String applicationName, MetadataEvent.Type registryType) { - this.stats.increment(registryType, applicationName); + public void increment(String applicationName, ApplicationType registryType) { + this.stats.incrementApp(registryType.getMetricsKey(), applicationName,1); + } + + public void incrementServiceKey(String applicationName, String serviceKey, ServiceType registryType, int size) { + this.stats.incrementServiceKey(registryType.getMetricsKey(), applicationName, serviceKey, size); } @Override public void addApplicationRT(String applicationName, String registryOpType, Long responseTime) { - stats.calcRt(applicationName, registryOpType, responseTime); + stats.calcApplicationRt(applicationName, registryOpType, responseTime); + } + + public void addServiceKeyRT(String applicationName, String serviceKey, String registryOpType, Long responseTime) { + stats.calcServiceKeyRt(applicationName, serviceKey, registryOpType, responseTime); } @Override @@ -82,9 +108,7 @@ public class MetadataMetricsCollector implements ApplicationMetricsCollector> numStats = new ConcurrentHashMap<>(); - public List> rtStats = new ArrayList<>(); - public static String OP_TYPE_PUSH = "push"; - public static String OP_TYPE_SUBSCRIBE = "subscribe"; - - public MetadataStatComposite() { - for (MetadataEvent.Type type : MetadataEvent.Type.values()) { - numStats.put(type, new ConcurrentHashMap<>()); - } - - rtStats.addAll(initStats(OP_TYPE_PUSH)); - rtStats.addAll(initStats(OP_TYPE_SUBSCRIBE)); - } - - private List> initStats(String registryOpType) { - List> singleRtStats = new ArrayList<>(); - singleRtStats.add(new AtomicLongContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.METRIC_RT_LAST))); - singleRtStats.add(new LongAccumulatorContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.METRIC_RT_MIN), new LongAccumulator(Long::min, Long.MAX_VALUE))); - singleRtStats.add(new LongAccumulatorContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.METRIC_RT_MAX), new LongAccumulator(Long::max, Long.MIN_VALUE))); - singleRtStats.add(new AtomicLongContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.METRIC_RT_SUM), (responseTime, longAccumulator) -> longAccumulator.addAndGet(responseTime))); - // AvgContainer is a special counter that stores the number of times but outputs function of sum/times - AtomicLongContainer avgContainer = new AtomicLongContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.METRIC_RT_AVG), (k, v) -> v.incrementAndGet()); - avgContainer.setValueSupplier(applicationName -> { - LongContainer totalContainer = rtStats.stream().filter(longContainer -> longContainer.isKeyWrapper(MetricsKey.METRIC_RT_SUM, registryOpType)).findFirst().get(); - AtomicLong totalRtTimes = avgContainer.get(applicationName); - AtomicLong totalRtSum = (AtomicLong) totalContainer.get(applicationName); - return totalRtSum.get() / totalRtTimes.get(); - }); - singleRtStats.add(avgContainer); - return singleRtStats; - } - - public void increment(MetadataEvent.Type type, String applicationName) { - if (!numStats.containsKey(type)) { - return; - } - numStats.get(type).computeIfAbsent(applicationName, k -> new AtomicLong(0L)).incrementAndGet(); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public void calcRt(String applicationName, String registryOpType, Long responseTime) { - for (LongContainer container : rtStats.stream().filter(longContainer -> longContainer.specifyType(registryOpType)).collect(Collectors.toList())) { - Number current = (Number) ConcurrentHashMapUtils.computeIfAbsent(container, applicationName, container.getInitFunc()); - container.getConsumerFunc().accept(responseTime, current); - } - } - - @Override - @SuppressWarnings("rawtypes") - public List exportNumMetrics() { - List list = new ArrayList<>(); - for (MetadataEvent.Type type : numStats.keySet()) { - Map stringAtomicLongMap = numStats.get(type); - for (String applicationName : stringAtomicLongMap.keySet()) { - list.add(convertToSample(applicationName, type, MetricsCategory.REGISTRY, stringAtomicLongMap.get(applicationName))); - } - } - return list; - } - - @Override - @SuppressWarnings("rawtypes") - public List exportRtMetrics() { - List list = new ArrayList<>(); - for (LongContainer rtContainer : rtStats) { - MetricsKeyWrapper metricsKeyWrapper = rtContainer.getMetricsKeyWrapper(); - for (Map.Entry entry : rtContainer.entrySet()) { - list.add(new GaugeMetricSample<>(metricsKeyWrapper.targetKey(), metricsKeyWrapper.targetDesc(), ApplicationMetric.getTagsByName(entry.getKey()), MetricsCategory.RT, entry.getKey().intern(), value -> rtContainer.getValueSupplier().apply(value.intern()))); - } - } - return list; - } - - @SuppressWarnings("rawtypes") - public GaugeMetricSample convertToSample(String applicationName, MetadataEvent.Type type, MetricsCategory category, AtomicLong targetNumber) { - return new GaugeMetricSample<>(type.getMetricsKey(), ApplicationMetric.getTagsByName(applicationName), category, targetNumber, AtomicLong::get); - } - -} diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataEvent.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataEvent.java index 8d68e5a189..b4b279457b 100644 --- a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataEvent.java +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataEvent.java @@ -17,93 +17,72 @@ package org.apache.dubbo.metrics.metadata.event; -import org.apache.dubbo.metrics.event.MetricsEvent; -import org.apache.dubbo.metrics.event.TimeCounter; +import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; +import org.apache.dubbo.metrics.event.TimeCounterEvent; +import org.apache.dubbo.metrics.exception.MetricsNeverHappenException; import org.apache.dubbo.metrics.metadata.collector.MetadataMetricsCollector; -import org.apache.dubbo.metrics.model.MetricsKey; -import org.apache.dubbo.metrics.model.TimePair; +import org.apache.dubbo.metrics.metadata.type.ApplicationType; +import org.apache.dubbo.metrics.metadata.type.ServiceType; +import org.apache.dubbo.metrics.model.key.MetricsLevel; +import org.apache.dubbo.metrics.model.key.TypeWrapper; import org.apache.dubbo.rpc.model.ApplicationModel; +import java.util.HashMap; +import java.util.Map; + +import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SERVICE; + /** * Registry related events */ -public class MetadataEvent extends MetricsEvent implements TimeCounter { - private final TimePair timePair; +public class MetadataEvent extends TimeCounterEvent { private final MetadataMetricsCollector collector; - private final boolean available; + private final Map attachment = new HashMap<>(8); - public MetadataEvent(ApplicationModel applicationModel, TimePair timePair) { + public MetadataEvent(ApplicationModel applicationModel, TypeWrapper typeWrapper) { super(applicationModel); - this.timePair = timePair; - this.collector = applicationModel.getBeanFactory().getBean(MetadataMetricsCollector.class); - this.available = this.collector != null && collector.isCollectEnabled(); + super.typeWrapper = typeWrapper; + ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); + if (beanFactory.isDestroyed()) { + this.collector = null; + } else { + this.collector = beanFactory.getBean(MetadataMetricsCollector.class); + super.setAvailable(this.collector != null && collector.isCollectEnabled()); + } + } + + @SuppressWarnings("unchecked") + public T getAttachmentValue(String key) { + if (!attachment.containsKey(key)) { + throw new MetricsNeverHappenException("Attachment key [" + key + "] not found"); + } + return (T) attachment.get(key); + } + + public void putAttachment(String key, Object value) { + attachment.put(key, value); } public ApplicationModel getSource() { - return (ApplicationModel) source; + return source; } public MetadataMetricsCollector getCollector() { return collector; } - public boolean isAvailable() { - return available; + public static MetadataEvent toPushEvent(ApplicationModel applicationModel) { + return new MetadataEvent(applicationModel, new TypeWrapper(MetricsLevel.APP, ApplicationType.P_TOTAL, ApplicationType.P_SUCCEED, ApplicationType.P_FAILED)); } - @Override - public TimePair getTimePair() { - return timePair; + public static MetadataEvent toSubscribeEvent(ApplicationModel applicationModel) { + return new MetadataEvent(applicationModel, new TypeWrapper(MetricsLevel.APP, ApplicationType.S_TOTAL, ApplicationType.S_SUCCEED, ApplicationType.S_FAILED)); } - public enum Type { - P_TOTAL(MetricsKey.METADATA_PUSH_METRIC_NUM), - P_SUCCEED(MetricsKey.METADATA_PUSH_METRIC_NUM_SUCCEED), - P_FAILED(MetricsKey.METADATA_PUSH_METRIC_NUM_FAILED), - - S_TOTAL(MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM), - S_SUCCEED(MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_SUCCEED), - S_FAILED(MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_FAILED), - - ; - - - private final MetricsKey metricsKey; - private final boolean isIncrement; - - - Type(MetricsKey metricsKey) { - this(metricsKey, true); - } - - Type(MetricsKey metricsKey, boolean isIncrement) { - this.metricsKey = metricsKey; - this.isIncrement = isIncrement; - } - - public MetricsKey getMetricsKey() { - return metricsKey; - } - - public boolean isIncrement() { - return isIncrement; - } - } - - public static class PushEvent extends MetadataEvent { - - public PushEvent(ApplicationModel applicationModel, TimePair timePair) { - super(applicationModel, timePair); - } - - } - - public static class SubscribeEvent extends MetadataEvent { - - public SubscribeEvent(ApplicationModel applicationModel, TimePair timePair) { - super(applicationModel, timePair); - } - + public static MetadataEvent toServiceSubscribeEvent(ApplicationModel applicationModel, String serviceKey) { + MetadataEvent metadataEvent = new MetadataEvent(applicationModel, new TypeWrapper(MetricsLevel.APP, ServiceType.S_P_TOTAL, ServiceType.S_P_SUCCEED, ServiceType.S_P_FAILED)); + metadataEvent.putAttachment(ATTACHMENT_KEY_SERVICE, serviceKey); + return metadataEvent; } } diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataListener.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataListener.java new file mode 100644 index 0000000000..68a8574fe7 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataListener.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.metadata.event; + +import org.apache.dubbo.metrics.event.MetricsEvent; +import org.apache.dubbo.metrics.listener.MetricsLifeListener; + +import java.util.function.BiConsumer; + +public abstract class MetadataListener implements MetricsLifeListener { + + private final Object enumType; + + public MetadataListener(Object enumType) { + this.enumType = enumType; + } + + @Override + public boolean isSupport(MetricsEvent event) { + return event.isAvailable() && event.isAssignableFrom(enumType); + } + + static MetadataListener onEvent(T enumType, BiConsumer postFunc) { + + return new MetadataListener(enumType) { + @Override + public void onEvent(MetadataEvent event) { + postFunc.accept(event, enumType); + } + }; + } + + static MetadataListener onFinish(T enumType, BiConsumer finishFunc) { + + return new MetadataListener(enumType) { + @Override + public void onEventFinish(MetadataEvent event) { + finishFunc.accept(event, enumType); + } + }; + } + + static MetadataListener onError(T enumType, BiConsumer errorFunc) { + + return new MetadataListener(enumType) { + @Override + public void onEventError(MetadataEvent event) { + errorFunc.accept(event, enumType); + } + }; + } +} diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataMetricsEventMulticaster.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataMetricsEventMulticaster.java index 92b3c96da3..2dd84a21d3 100644 --- a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataMetricsEventMulticaster.java +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataMetricsEventMulticaster.java @@ -17,15 +17,71 @@ package org.apache.dubbo.metrics.metadata.event; +import org.apache.dubbo.metrics.MetricsConstants; import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster; +import org.apache.dubbo.metrics.metadata.type.ApplicationType; +import org.apache.dubbo.metrics.metadata.type.ServiceType; + +import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_PUSH; +import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_STORE_PROVIDER_INTERFACE; +import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_SUBSCRIBE; public final class MetadataMetricsEventMulticaster extends SimpleMetricsEventMulticaster { public MetadataMetricsEventMulticaster() { - super.addListener(new MetricsPushListener()); - super.addListener(new MetricsSubscribeListener()); + // MetricsPushListener + super.addListener(onPostEventBuild(ApplicationType.P_TOTAL)); + super.addListener(onFinishEventBuild(ApplicationType.P_SUCCEED, OP_TYPE_PUSH.getType())); + super.addListener(onErrorEventBuild(ApplicationType.P_FAILED, OP_TYPE_PUSH.getType())); + + // MetricsSubscribeListener + super.addListener(onPostEventBuild(ApplicationType.S_TOTAL)); + super.addListener(onFinishEventBuild(ApplicationType.S_SUCCEED, OP_TYPE_SUBSCRIBE.getType())); + super.addListener(onErrorEventBuild(ApplicationType.S_FAILED, OP_TYPE_SUBSCRIBE.getType())); + + // StoreProviderMetadataListener + super.addListener(MetadataListener.onEvent(ServiceType.S_P_TOTAL, + this::incrServiceKey + )); + super.addListener(MetadataListener.onFinish(ServiceType.S_P_SUCCEED, + this::incrAndRt + )); + super.addListener(MetadataListener.onError(ServiceType.S_P_FAILED, + this::incrAndRt + )); - setAvailable(); } + private void incrAndRt(MetadataEvent event, ServiceType type) { + incrServiceKey(event, type); + event.getCollector().addServiceKeyRT(event.getSource().getApplicationName(), event.getAttachmentValue(MetricsConstants.ATTACHMENT_KEY_SERVICE), OP_TYPE_STORE_PROVIDER_INTERFACE.getType(), event.getTimePair().calc()); + } + + private void incrServiceKey(MetadataEvent event, ServiceType type) { + event.getCollector().incrementServiceKey(event.getSource().getApplicationName(), event.getAttachmentValue(MetricsConstants.ATTACHMENT_KEY_SERVICE), type, 1); + } + + + private MetadataListener onPostEventBuild(ApplicationType applicationType) { + return MetadataListener.onEvent(applicationType, + (event, type) -> event.getCollector().increment(event.getSource().getApplicationName(), type) + ); + } + + private MetadataListener onFinishEventBuild(ApplicationType applicationType, String registryOpType) { + return MetadataListener.onFinish(applicationType, + (event, type) -> incrAndRt(event, applicationType, registryOpType) + ); + } + + private MetadataListener onErrorEventBuild(ApplicationType applicationType, String registryOpType) { + return MetadataListener.onError(applicationType, + (event, type) -> incrAndRt(event, applicationType, registryOpType) + ); + } + + private void incrAndRt(MetadataEvent event, ApplicationType applicationType, String registryOpType) { + event.getCollector().increment(event.getSource().getApplicationName(), applicationType); + event.getCollector().addApplicationRT(event.getSource().getApplicationName(), registryOpType, event.getTimePair().calc()); + } } diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsPushListener.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsPushListener.java deleted file mode 100644 index 1c3352cf50..0000000000 --- a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsPushListener.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.dubbo.metrics.metadata.event; - -import org.apache.dubbo.metrics.event.MetricsEvent; -import org.apache.dubbo.metrics.listener.MetricsLifeListener; - -import static org.apache.dubbo.metrics.metadata.collector.stat.MetadataStatComposite.OP_TYPE_PUSH; - -public class MetricsPushListener implements MetricsLifeListener { - - - @Override - public boolean isSupport(MetricsEvent event) { - return event instanceof MetadataEvent.PushEvent && ((MetadataEvent) event).isAvailable(); - } - - @Override - public void onEvent(MetadataEvent.PushEvent event) { - event.getCollector().increment(event.getSource().getApplicationName(), MetadataEvent.Type.P_TOTAL); - } - - @Override - public void onEventFinish(MetadataEvent.PushEvent event) { - event.getCollector().increment(event.getSource().getApplicationName(), MetadataEvent.Type.P_SUCCEED); - event.getCollector().addApplicationRT(event.getSource().getApplicationName(), OP_TYPE_PUSH, event.getTimePair().calc()); - } - - @Override - public void onEventError(MetadataEvent.PushEvent event) { - event.getCollector().increment(event.getSource().getApplicationName(), MetadataEvent.Type.P_FAILED); - event.getCollector().addApplicationRT(event.getSource().getApplicationName(), OP_TYPE_PUSH, event.getTimePair().calc()); - } -} diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsSubscribeListener.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsSubscribeListener.java deleted file mode 100644 index 1e17acc778..0000000000 --- a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsSubscribeListener.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.dubbo.metrics.metadata.event; - -import org.apache.dubbo.metrics.event.MetricsEvent; -import org.apache.dubbo.metrics.listener.MetricsLifeListener; - -import static org.apache.dubbo.metrics.metadata.collector.stat.MetadataStatComposite.OP_TYPE_SUBSCRIBE; - -public class MetricsSubscribeListener implements MetricsLifeListener { - - @Override - public boolean isSupport(MetricsEvent event) { - return event instanceof MetadataEvent.SubscribeEvent && ((MetadataEvent) event).isAvailable(); - } - - @Override - public void onEvent(MetadataEvent.SubscribeEvent event) { - event.getCollector().increment(event.getSource().getApplicationName(), MetadataEvent.Type.S_TOTAL); - } - - @Override - public void onEventFinish(MetadataEvent.SubscribeEvent event) { - event.getCollector().increment(event.getSource().getApplicationName(), MetadataEvent.Type.S_SUCCEED); - event.getCollector().addApplicationRT(event.getSource().getApplicationName(), OP_TYPE_SUBSCRIBE, event.getTimePair().calc()); - } - - @Override - public void onEventError(MetadataEvent.SubscribeEvent event) { - event.getCollector().increment(event.getSource().getApplicationName(), MetadataEvent.Type.S_FAILED); - event.getCollector().addApplicationRT(event.getSource().getApplicationName(), OP_TYPE_SUBSCRIBE, event.getTimePair().calc()); - } - -} diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/type/ApplicationType.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/type/ApplicationType.java new file mode 100644 index 0000000000..ca00398738 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/type/ApplicationType.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.metadata.type; + + +import org.apache.dubbo.metrics.model.key.MetricsKey; + +public enum ApplicationType { + P_TOTAL(MetricsKey.METADATA_PUSH_METRIC_NUM), + P_SUCCEED(MetricsKey.METADATA_PUSH_METRIC_NUM_SUCCEED), + P_FAILED(MetricsKey.METADATA_PUSH_METRIC_NUM_FAILED), + + S_TOTAL(MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM), + S_SUCCEED(MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_SUCCEED), + S_FAILED(MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_FAILED), + + ; + private final MetricsKey metricsKey; + private final boolean isIncrement; + + ApplicationType(MetricsKey metricsKey) { + this(metricsKey, true); + } + + ApplicationType(MetricsKey metricsKey, boolean isIncrement) { + this.metricsKey = metricsKey; + this.isIncrement = isIncrement; + } + + public MetricsKey getMetricsKey() { + return metricsKey; + } + + public boolean isIncrement() { + return isIncrement; + } +} diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/type/ServiceType.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/type/ServiceType.java new file mode 100644 index 0000000000..fddcfad104 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/type/ServiceType.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.metadata.type; + +import org.apache.dubbo.metrics.model.key.MetricsKey; + +public enum ServiceType { + + S_P_TOTAL(MetricsKey.STORE_PROVIDER_METADATA), + S_P_SUCCEED(MetricsKey.STORE_PROVIDER_METADATA_SUCCEED), + S_P_FAILED(MetricsKey.STORE_PROVIDER_METADATA_FAILED), + + ; + + private final MetricsKey metricsKey; + private final boolean isIncrement; + + + ServiceType(MetricsKey metricsKey) { + this(metricsKey, true); + } + + ServiceType(MetricsKey metricsKey, boolean isIncrement) { + this.metricsKey = metricsKey; + this.isIncrement = isIncrement; + } + + public MetricsKey getMetricsKey() { + return metricsKey; + } + + public boolean isIncrement() { + return isIncrement; + } +} diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/test/java/org/apache/dubbo/metrics/metadata/MetadataMetricsCollectorTest.java b/dubbo-metrics/dubbo-metrics-metadata/src/test/java/org/apache/dubbo/metrics/metadata/MetadataMetricsCollectorTest.java index 0b6d44c2b1..dd2662db64 100644 --- a/dubbo-metrics/dubbo-metrics-metadata/src/test/java/org/apache/dubbo/metrics/metadata/MetadataMetricsCollectorTest.java +++ b/dubbo-metrics/dubbo-metrics-metadata/src/test/java/org/apache/dubbo/metrics/metadata/MetadataMetricsCollectorTest.java @@ -18,11 +18,12 @@ package org.apache.dubbo.metrics.metadata; import org.apache.dubbo.config.ApplicationConfig; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; +import org.apache.dubbo.metrics.event.MetricsDispatcher; +import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.metadata.collector.MetadataMetricsCollector; import org.apache.dubbo.metrics.metadata.event.MetadataEvent; -import org.apache.dubbo.metrics.model.MetricsKey; -import org.apache.dubbo.metrics.model.MetricsKeyWrapper; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.TimePair; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; @@ -36,11 +37,13 @@ import org.junit.jupiter.api.Test; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; -import static org.apache.dubbo.metrics.metadata.collector.stat.MetadataStatComposite.OP_TYPE_PUSH; -import static org.apache.dubbo.metrics.metadata.collector.stat.MetadataStatComposite.OP_TYPE_SUBSCRIBE; +import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_PUSH; +import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_STORE_PROVIDER_INTERFACE; +import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_SUBSCRIBE; class MetadataMetricsCollectorTest { @@ -64,32 +67,44 @@ class MetadataMetricsCollectorTest { } @Test - void testPushMetrics() throws InterruptedException { + void testPushMetrics() { - TimePair timePair = TimePair.start(); - GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getOrRegisterBean(GlobalMetricsEventMulticaster.class); + applicationModel.getBeanFactory().getOrRegisterBean(MetricsDispatcher.class); MetadataMetricsCollector collector = applicationModel.getBeanFactory().getOrRegisterBean(MetadataMetricsCollector.class); collector.setCollectEnabled(true); - eventMulticaster.publishEvent(new MetadataEvent.PushEvent(applicationModel, timePair)); - List metricSamples = collector.collect(); + MetadataEvent pushEvent = MetadataEvent.toPushEvent(applicationModel); + MetricsEventBus.post(pushEvent, + () -> { + List metricSamples = collector.collect(); - // push success +1 - Assertions.assertEquals(1, metricSamples.size()); - Assertions.assertTrue(metricSamples.get(0) instanceof GaugeMetricSample); - Assertions.assertEquals(metricSamples.get(0).getName(), MetricsKey.METADATA_PUSH_METRIC_NUM.getName()); + // push success +1 + Assertions.assertEquals(1, metricSamples.size()); + Assertions.assertTrue(metricSamples.get(0) instanceof GaugeMetricSample); + Assertions.assertEquals(metricSamples.get(0).getName(), MetricsKey.METADATA_PUSH_METRIC_NUM.getName()); + return null; + } + ); - eventMulticaster.publishFinishEvent(new MetadataEvent.PushEvent(applicationModel, timePair)); // push finish rt +1 - metricSamples = collector.collect(); + List metricSamples = collector.collect(); //num(total+success) + rt(5) = 7 Assertions.assertEquals(7, metricSamples.size()); - long c1 = timePair.calc(); - TimePair lastTimePair = TimePair.start(); - eventMulticaster.publishEvent(new MetadataEvent.PushEvent(applicationModel, lastTimePair)); - Thread.sleep(50); + long c1 = pushEvent.getTimePair().calc(); + + pushEvent = MetadataEvent.toPushEvent(applicationModel); + TimePair lastTimePair = pushEvent.getTimePair(); + MetricsEventBus.post(pushEvent, + () -> { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }, Objects::nonNull + ); // push error rt +1 - eventMulticaster.publishErrorEvent(new MetadataEvent.PushEvent(applicationModel, lastTimePair)); long c2 = lastTimePair.calc(); metricSamples = collector.collect(); @@ -105,40 +120,52 @@ class MetadataMetricsCollectorTest { @SuppressWarnings("rawtypes") Map sampleMap = metricSamples.stream().collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_PUSH, MetricsKey.METRIC_RT_LAST).targetKey()), lastTimePair.calc()); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_PUSH, MetricsKey.METRIC_RT_MIN).targetKey()), Math.min(c1, c2)); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_PUSH, MetricsKey.METRIC_RT_MAX).targetKey()), Math.max(c1, c2)); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_PUSH, MetricsKey.METRIC_RT_AVG).targetKey()), (c1 + c2) / 2); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_PUSH, MetricsKey.METRIC_RT_SUM).targetKey()), c1 + c2); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_PUSH).targetKey()), lastTimePair.calc()); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_PUSH).targetKey()), Math.min(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_PUSH).targetKey()), Math.max(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_PUSH).targetKey()), (c1 + c2) / 2); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_PUSH).targetKey()), c1 + c2); } @Test - void testSubscribeMetrics() throws InterruptedException { + void testSubscribeMetrics() { - TimePair timePair = TimePair.start(); - GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getOrRegisterBean(GlobalMetricsEventMulticaster.class); + applicationModel.getBeanFactory().getOrRegisterBean(MetricsDispatcher.class); MetadataMetricsCollector collector = applicationModel.getBeanFactory().getOrRegisterBean(MetadataMetricsCollector.class); collector.setCollectEnabled(true); - eventMulticaster.publishEvent(new MetadataEvent.SubscribeEvent(applicationModel, timePair)); - List metricSamples = collector.collect(); + MetadataEvent subscribeEvent = MetadataEvent.toSubscribeEvent(applicationModel); + MetricsEventBus.post(subscribeEvent, + () -> { + List metricSamples = collector.collect(); - // push success +1 - Assertions.assertEquals(1, metricSamples.size()); - Assertions.assertTrue(metricSamples.get(0) instanceof GaugeMetricSample); - Assertions.assertEquals(metricSamples.get(0).getName(), MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM.getName()); + // push success +1 + Assertions.assertEquals(1, metricSamples.size()); + Assertions.assertTrue(metricSamples.get(0) instanceof GaugeMetricSample); + Assertions.assertEquals(metricSamples.get(0).getName(), MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM.getName()); + return null; + } + ); + long c1 = subscribeEvent.getTimePair().calc(); - eventMulticaster.publishFinishEvent(new MetadataEvent.SubscribeEvent(applicationModel, timePair)); // push finish rt +1 - metricSamples = collector.collect(); + List metricSamples = collector.collect(); //num(total+success) + rt(5) = 7 Assertions.assertEquals(7, metricSamples.size()); - long c1 = timePair.calc(); - TimePair lastTimePair = TimePair.start(); - eventMulticaster.publishEvent(new MetadataEvent.SubscribeEvent(applicationModel, lastTimePair)); - Thread.sleep(50); + subscribeEvent = MetadataEvent.toSubscribeEvent(applicationModel); + TimePair lastTimePair = subscribeEvent.getTimePair(); + MetricsEventBus.post(subscribeEvent, + () -> { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }, Objects::nonNull + ); + // push error rt +1 - eventMulticaster.publishErrorEvent(new MetadataEvent.SubscribeEvent(applicationModel, lastTimePair)); long c2 = lastTimePair.calc(); metricSamples = collector.collect(); @@ -154,12 +181,75 @@ class MetadataMetricsCollectorTest { @SuppressWarnings("rawtypes") Map sampleMap = metricSamples.stream().collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_SUBSCRIBE, MetricsKey.METRIC_RT_LAST).targetKey()), lastTimePair.calc()); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_SUBSCRIBE, MetricsKey.METRIC_RT_MIN).targetKey()), Math.min(c1, c2)); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_SUBSCRIBE, MetricsKey.METRIC_RT_MAX).targetKey()), Math.max(c1, c2)); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_SUBSCRIBE, MetricsKey.METRIC_RT_AVG).targetKey()), (c1 + c2) / 2); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_SUBSCRIBE, MetricsKey.METRIC_RT_SUM).targetKey()), c1 + c2); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_SUBSCRIBE).targetKey()), lastTimePair.calc()); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_SUBSCRIBE).targetKey()), Math.min(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_SUBSCRIBE).targetKey()), Math.max(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_SUBSCRIBE).targetKey()), (c1 + c2) / 2); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_SUBSCRIBE).targetKey()), c1 + c2); } + @Test + void testStoreProviderMetadataMetrics() { + + applicationModel.getBeanFactory().getOrRegisterBean(MetricsDispatcher.class); + MetadataMetricsCollector collector = applicationModel.getBeanFactory().getOrRegisterBean(MetadataMetricsCollector.class); + collector.setCollectEnabled(true); + + String serviceKey = "store.provider.test"; + MetadataEvent metadataEvent = MetadataEvent.toServiceSubscribeEvent(applicationModel, serviceKey); + MetricsEventBus.post(metadataEvent, + () -> { + List metricSamples = collector.collect(); + + // push success +1 + Assertions.assertEquals(1, metricSamples.size()); + Assertions.assertTrue(metricSamples.get(0) instanceof GaugeMetricSample); + Assertions.assertEquals(metricSamples.get(0).getName(), MetricsKey.STORE_PROVIDER_METADATA.getName()); + Assertions.assertEquals(metricSamples.get(0).getTags().get("interface"), serviceKey); + return null; + } + ); + + // push finish rt +1 + List metricSamples = collector.collect(); + //num(total+success) + rt(5) = 7 + Assertions.assertEquals(7, metricSamples.size()); + long c1 = metadataEvent.getTimePair().calc(); + metadataEvent = MetadataEvent.toServiceSubscribeEvent(applicationModel, serviceKey); + TimePair lastTimePair = metadataEvent.getTimePair(); + MetricsEventBus.post(metadataEvent, + () -> { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }, Objects::nonNull + ); + // push error rt +1 + long c2 = lastTimePair.calc(); + + metricSamples = collector.collect(); + + // num(total+success+error) + rt(5) + Assertions.assertEquals(8, metricSamples.size()); + + // calc rt + for (MetricSample sample : metricSamples) { + Map tags = sample.getTags(); + Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); + } + + @SuppressWarnings("rawtypes") + Map sampleMap = metricSamples.stream().collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); + + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_STORE_PROVIDER_INTERFACE).targetKey()), lastTimePair.calc()); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_STORE_PROVIDER_INTERFACE).targetKey()), Math.min(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_STORE_PROVIDER_INTERFACE).targetKey()), Math.max(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_STORE_PROVIDER_INTERFACE).targetKey()), (c1 + c2) / 2); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_STORE_PROVIDER_INTERFACE).targetKey()), c1 + c2); + } + } diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/test/java/org/apache/dubbo/metrics/metadata/MetadataStatCompositeTest.java b/dubbo-metrics/dubbo-metrics-metadata/src/test/java/org/apache/dubbo/metrics/metadata/MetadataStatCompositeTest.java index 66906decce..9f8cd91c51 100644 --- a/dubbo-metrics/dubbo-metrics-metadata/src/test/java/org/apache/dubbo/metrics/metadata/MetadataStatCompositeTest.java +++ b/dubbo-metrics/dubbo-metrics-metadata/src/test/java/org/apache/dubbo/metrics/metadata/MetadataStatCompositeTest.java @@ -17,8 +17,11 @@ package org.apache.dubbo.metrics.metadata; -import org.apache.dubbo.metrics.metadata.collector.stat.MetadataStatComposite; -import org.apache.dubbo.metrics.metadata.event.MetadataEvent; +import org.apache.dubbo.metrics.data.ApplicationStatComposite; +import org.apache.dubbo.metrics.data.BaseStatComposite; +import org.apache.dubbo.metrics.data.RtStatComposite; +import org.apache.dubbo.metrics.data.ServiceStatComposite; +import org.apache.dubbo.metrics.metadata.type.ApplicationType; import org.apache.dubbo.metrics.model.container.LongContainer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -27,21 +30,32 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -import static org.apache.dubbo.metrics.metadata.collector.stat.MetadataStatComposite.OP_TYPE_SUBSCRIBE; +import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_PUSH; +import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_STORE_PROVIDER_INTERFACE; +import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_SUBSCRIBE; public class MetadataStatCompositeTest { private final String applicationName = "app1"; + private final BaseStatComposite statComposite = new BaseStatComposite() { + @Override + protected void init(ApplicationStatComposite applicationStatComposite, ServiceStatComposite + serviceStatComposite, RtStatComposite rtStatComposite) { + applicationStatComposite.init(MetadataMetricsConstants.appKeys); + serviceStatComposite.init(MetadataMetricsConstants.serviceKeys); + rtStatComposite.init(OP_TYPE_PUSH, OP_TYPE_SUBSCRIBE, OP_TYPE_STORE_PROVIDER_INTERFACE); + } + }; + @Test void testInit() { - MetadataStatComposite statComposite = new MetadataStatComposite(); - Assertions.assertEquals(statComposite.numStats.size(), MetadataEvent.Type.values().length); - //(rt)5 * (push,subscribe)2 - Assertions.assertEquals(5 * 2, statComposite.rtStats.size()); - statComposite.numStats.values().forEach((v -> + Assertions.assertEquals(statComposite.getApplicationStatComposite().getApplicationNumStats().size(), ApplicationType.values().length); + //(rt)5 * (push,subscribe,service)3 + Assertions.assertEquals(5 * 3, statComposite.getRtStatComposite().getRtStats().size()); + statComposite.getApplicationStatComposite().getApplicationNumStats().values().forEach((v -> Assertions.assertEquals(v, new ConcurrentHashMap<>()))); - statComposite.rtStats.forEach(rtContainer -> + statComposite.getRtStatComposite().getRtStats().forEach(rtContainer -> { for (Map.Entry entry : rtContainer.entrySet()) { Assertions.assertEquals(0L, rtContainer.getValueSupplier().apply(entry.getKey())); @@ -51,17 +65,15 @@ public class MetadataStatCompositeTest { @Test void testIncrement() { - MetadataStatComposite statComposite = new MetadataStatComposite(); - statComposite.increment(MetadataEvent.Type.P_TOTAL, applicationName); - Assertions.assertEquals(1L, statComposite.numStats.get(MetadataEvent.Type.P_TOTAL).get(applicationName).get()); + statComposite.incrementApp(ApplicationType.P_TOTAL.getMetricsKey(), applicationName, 1); + Assertions.assertEquals(1L, statComposite.getApplicationStatComposite().getApplicationNumStats().get(ApplicationType.P_TOTAL.getMetricsKey()).get(applicationName).get()); } @Test void testCalcRt() { - MetadataStatComposite statComposite = new MetadataStatComposite(); - statComposite.calcRt(applicationName, OP_TYPE_SUBSCRIBE, 10L); - Assertions.assertTrue(statComposite.rtStats.stream().anyMatch(longContainer -> longContainer.specifyType(OP_TYPE_SUBSCRIBE))); - Optional> subContainer = statComposite.rtStats.stream().filter(longContainer -> longContainer.specifyType(OP_TYPE_SUBSCRIBE)).findFirst(); + statComposite.calcApplicationRt(applicationName, OP_TYPE_SUBSCRIBE.getType(), 10L); + Assertions.assertTrue(statComposite.getRtStatComposite().getRtStats().stream().anyMatch(longContainer -> longContainer.specifyType(OP_TYPE_SUBSCRIBE.getType()))); + Optional> subContainer = statComposite.getRtStatComposite().getRtStats().stream().filter(longContainer -> longContainer.specifyType(OP_TYPE_SUBSCRIBE.getType())).findFirst(); subContainer.ifPresent(v -> Assertions.assertEquals(10L, v.get(applicationName).longValue())); } } diff --git a/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterCmd.java b/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterCmd.java index 9bea988fb1..b41d68c998 100644 --- a/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterCmd.java +++ b/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterCmd.java @@ -98,7 +98,7 @@ public class PrometheusMetricsReporterCmd implements BaseCommand { for (ApplicationModel model : models) { appResultMap.put(model.getApplicationName(), getResponseByApplication(model)); } - return JsonUtils.getJson().toJson(appResultMap); + return JsonUtils.toJson(appResultMap); } @Override diff --git a/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterTest.java b/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterTest.java index 1c43927713..ebaf276001 100644 --- a/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterTest.java +++ b/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterTest.java @@ -68,7 +68,7 @@ class PrometheusMetricsReporterTest { @Test void testJvmMetrics() { - metricsConfig.setEnableJvmMetrics(true); + metricsConfig.setEnableJvm(true); String name = "metrics-test"; ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(new ApplicationConfig(name)); @@ -94,7 +94,7 @@ class PrometheusMetricsReporterTest { exporter.setEnabled(true); prometheusConfig.setExporter(exporter); metricsConfig.setPrometheus(prometheusConfig); - metricsConfig.setEnableJvmMetrics(true); + metricsConfig.setEnableJvm(true); ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(new ApplicationConfig("metrics-test")); PrometheusMetricsReporter reporter = new PrometheusMetricsReporter(metricsConfig.toUrl(), applicationModel); diff --git a/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsThreadPoolTest.java b/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsThreadPoolTest.java new file mode 100644 index 0000000000..867b47a518 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsThreadPoolTest.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metrics.prometheus; +import com.sun.net.httpserver.HttpServer; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.MetricsConfig; +import org.apache.dubbo.config.nested.PrometheusConfig; +import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; +import org.apache.dubbo.metrics.collector.sample.ThreadRejectMetricsCountSampler; +import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; +import org.apache.dubbo.metrics.model.sample.MetricSample; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.FrameworkModel; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_THREAD_NAME; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME; +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP; +import static org.apache.dubbo.common.utils.NetUtils.getLocalHost; +import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName; + + +public class PrometheusMetricsThreadPoolTest { + + private FrameworkModel frameworkModel; + + private ApplicationModel applicationModel; + + private MetricsConfig metricsConfig; + + DefaultMetricsCollector metricsCollector; + + @BeforeEach + public void setup() { + applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig config = new ApplicationConfig(); + config.setName("MockMetrics"); + + applicationModel.getApplicationConfigManager().setApplication(config); + metricsConfig = new MetricsConfig(); + metricsConfig.setProtocol(PROTOCOL_PROMETHEUS); + frameworkModel = FrameworkModel.defaultModel(); + metricsCollector = frameworkModel.getBeanFactory().getOrRegisterBean(DefaultMetricsCollector.class); + } + + @AfterEach + public void teardown() { + applicationModel.destroy(); + } + + @Test + void testExporterThreadpoolName() { + int port = 30899; + PrometheusConfig prometheusConfig = new PrometheusConfig(); + PrometheusConfig.Exporter exporter = new PrometheusConfig.Exporter(); + exporter.setMetricsPort(port); + exporter.setEnabled(true); + exporter.setMetricsPath("/metrics"); + + prometheusConfig.setExporter(exporter); + metricsConfig.setPrometheus(prometheusConfig); + metricsConfig.setEnableJvm(false); + metricsCollector.setCollectEnabled(true); + metricsConfig.setEnableThreadpool(true); + metricsCollector.collectApplication(applicationModel); + PrometheusMetricsReporter reporter = new PrometheusMetricsReporter(metricsConfig.toUrl(), applicationModel); + reporter.init(); + exportHttpServer(reporter,port); + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if(metricsConfig.getEnableThreadpool()) { + metricsCollector.registryDefaultSample(); + } + try (CloseableHttpClient client = HttpClients.createDefault()) { + HttpGet request = new HttpGet("http://localhost:" + port + "/metrics"); + CloseableHttpResponse response = client.execute(request); + InputStream inputStream = response.getEntity().getContent(); + String text = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines().collect(Collectors.joining("\n")); + Assertions.assertTrue(text.contains("dubbo_thread_pool_core_size")); + Assertions.assertTrue(text.contains("dubbo_thread_pool_thread_count")); + } catch (Exception e) { + Assertions.fail(e); + } finally { + reporter.destroy(); + } + } + + + private void exportHttpServer(PrometheusMetricsReporter reporter, int port) { + try { + HttpServer prometheusExporterHttpServer = HttpServer.create(new InetSocketAddress(port), 0); + prometheusExporterHttpServer.createContext("/metrics", httpExchange -> { + reporter.refreshData(); + String response = reporter.getPrometheusRegistry().scrape(); + httpExchange.sendResponseHeaders(200, response.getBytes().length); + try (OutputStream os = httpExchange.getResponseBody()) { + os.write(response.getBytes()); + } + }); + Thread httpServerThread = new Thread(prometheusExporterHttpServer::start); + httpServerThread.start(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + + @Test + @SuppressWarnings("rawtypes") + void testThreadPoolRejectMetrics() { + DefaultMetricsCollector collector = new DefaultMetricsCollector(); + collector.setCollectEnabled(true); + collector.setApplicationName(applicationModel.getApplicationName()); + String threadPoolExecutorName="DubboServerHandler-20816"; + ThreadRejectMetricsCountSampler threadRejectMetricsCountSampler=new ThreadRejectMetricsCountSampler(collector); + threadRejectMetricsCountSampler.incOnEvent(threadPoolExecutorName,threadPoolExecutorName); + threadRejectMetricsCountSampler.addMetricName(threadPoolExecutorName); + List samples = collector.collect(); + for (MetricSample sample : samples) { + Assertions.assertTrue(sample instanceof GaugeMetricSample); + GaugeMetricSample gaugeSample = (GaugeMetricSample) sample; + Map tags = gaugeSample.getTags(); + Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), "MockMetrics"); + Assertions.assertEquals(tags.get(TAG_THREAD_NAME), threadPoolExecutorName); + Assertions.assertEquals(tags.get(TAG_IP), getLocalHost()); + Assertions.assertEquals(tags.get(TAG_HOSTNAME), getLocalHostName()); + Assertions.assertEquals(gaugeSample.applyAsLong(), 1); + } + } + +} diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/RegistryMetricsConstants.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/RegistryMetricsConstants.java new file mode 100644 index 0000000000..28b012076a --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/RegistryMetricsConstants.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.registry; + +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsLevel; +import org.apache.dubbo.metrics.model.key.MetricsPlaceType; +import org.apache.dubbo.metrics.registry.event.type.ApplicationType; +import org.apache.dubbo.metrics.registry.event.type.ServiceType; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public interface RegistryMetricsConstants { + + MetricsPlaceType OP_TYPE_REGISTER = MetricsPlaceType.of("register", MetricsLevel.APP); + MetricsPlaceType OP_TYPE_SUBSCRIBE = MetricsPlaceType.of("subscribe", MetricsLevel.APP); + MetricsPlaceType OP_TYPE_NOTIFY = MetricsPlaceType.of("notify", MetricsLevel.APP); + MetricsPlaceType OP_TYPE_REGISTER_SERVICE = MetricsPlaceType.of("register.service", MetricsLevel.SERVICE); + MetricsPlaceType OP_TYPE_SUBSCRIBE_SERVICE = MetricsPlaceType.of("subscribe.service", MetricsLevel.SERVICE); + + // App-level + List appKeys = Arrays.stream(ApplicationType.values()).map(ApplicationType::getMetricsKey).collect(Collectors.toList()); + + // Service-level + List serviceKeys = Arrays.stream(ServiceType.values()).map(ServiceType::getMetricsKey).collect(Collectors.toList()); + +} diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryMetricsCollector.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryMetricsCollector.java index b079c5c0ca..f55d580c09 100644 --- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryMetricsCollector.java +++ b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryMetricsCollector.java @@ -21,12 +21,19 @@ import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.metrics.collector.ApplicationMetricsCollector; import org.apache.dubbo.metrics.collector.MetricsCollector; +import org.apache.dubbo.metrics.data.ApplicationStatComposite; +import org.apache.dubbo.metrics.data.BaseStatComposite; +import org.apache.dubbo.metrics.data.RtStatComposite; +import org.apache.dubbo.metrics.data.ServiceStatComposite; import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.event.MetricsEventMulticaster; +import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.sample.MetricSample; -import org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite; +import org.apache.dubbo.metrics.registry.RegistryMetricsConstants; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.metrics.registry.event.RegistryMetricsEventMulticaster; +import org.apache.dubbo.metrics.registry.event.type.ApplicationType; +import org.apache.dubbo.metrics.registry.event.type.ServiceType; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; @@ -34,20 +41,33 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_NOTIFY; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER_SERVICE; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE_SERVICE; + /** * Registry implementation of {@link MetricsCollector} */ @Activate -public class RegistryMetricsCollector implements ApplicationMetricsCollector { +public class RegistryMetricsCollector implements ApplicationMetricsCollector { private Boolean collectEnabled = null; - private final RegistryStatComposite stats; + private final BaseStatComposite stats; private final MetricsEventMulticaster registryMulticaster; private final ApplicationModel applicationModel; public RegistryMetricsCollector(ApplicationModel applicationModel) { - this.stats = new RegistryStatComposite(); + this.stats = new BaseStatComposite() { + @Override + protected void init(ApplicationStatComposite applicationStatComposite, ServiceStatComposite serviceStatComposite, RtStatComposite rtStatComposite) { + applicationStatComposite.init(RegistryMetricsConstants.appKeys); + serviceStatComposite.init(RegistryMetricsConstants.serviceKeys); + rtStatComposite.init(OP_TYPE_REGISTER, OP_TYPE_SUBSCRIBE, OP_TYPE_NOTIFY, OP_TYPE_REGISTER_SERVICE, OP_TYPE_SUBSCRIBE_SERVICE); + } + }; this.registryMulticaster = new RegistryMetricsEventMulticaster(); this.applicationModel = applicationModel; } @@ -62,32 +82,27 @@ public class RegistryMetricsCollector implements ApplicationMetricsCollector setCollectEnabled(metricsConfig.getEnableRegistryMetrics())); + configManager.getMetrics().ifPresent(metricsConfig -> setCollectEnabled(metricsConfig.getEnableRegistry())); } - return Optional.ofNullable(collectEnabled).orElse(false); + return Optional.ofNullable(collectEnabled).orElse(true); } - public void setNum(RegistryEvent.ServiceType registryType, String applicationName, Map lastNumMap) { + public void setNum(ServiceType registryType, String applicationName, Map lastNumMap) { lastNumMap.forEach((serviceKey, num) -> - this.stats.setServiceKey(registryType, applicationName, serviceKey, num)); + this.stats.setServiceKey(registryType.getMetricsKey(), applicationName, serviceKey, num)); } - public void setNum(RegistryEvent.ApplicationType registryType, String applicationName, Integer num) { - this.stats.setApplicationKey(registryType, applicationName, num); + public void setNum(ApplicationType registryType, String applicationName, Integer num) { + this.stats.setApplicationKey(registryType.getMetricsKey(), applicationName, num); } - @Override - public void increment(String applicationName, RegistryEvent.ApplicationType registryType) { - this.stats.increment(registryType, applicationName); + public void increment(String applicationName, ApplicationType registryType) { + this.stats.incrementApp(registryType.getMetricsKey(), applicationName, 1); } - public void increment(String applicationName, RegistryEvent.ApplicationType registryType, int size) { - this.stats.incrementSize(registryType, applicationName, size); - } - - public void incrementServiceKey(String applicationName, String serviceKey, RegistryEvent.ServiceType registryType, int size) { - this.stats.incrementServiceKey(registryType, applicationName, serviceKey, size); + public void incrementServiceKey(String applicationName, String serviceKey, ServiceType registryType, int size) { + this.stats.incrementServiceKey(registryType.getMetricsKey(), applicationName, serviceKey, size); } @Override @@ -105,10 +120,7 @@ public class RegistryMetricsCollector implements ApplicationMetricsCollector> applicationNumStats = new ConcurrentHashMap<>(); - public Map> serviceNumStats = new ConcurrentHashMap<>(); - public Map> skStats = new ConcurrentHashMap<>(); - public List> appRtStats = new ArrayList<>(); - public List> serviceRtStats = new ArrayList<>(); - public static String OP_TYPE_REGISTER = "register"; - public static String OP_TYPE_SUBSCRIBE = "subscribe"; - public static String OP_TYPE_NOTIFY = "notify"; - public static String OP_TYPE_REGISTER_SERVICE = "register.service"; - public static String OP_TYPE_SUBSCRIBE_SERVICE = "subscribe.service"; - - public RegistryStatComposite() { - for (RegistryEvent.ApplicationType type : RegistryEvent.ApplicationType.values()) { - // Application key and increment val - applicationNumStats.put(type, new ConcurrentHashMap<>()); - } - - for (RegistryEvent.ServiceType type : RegistryEvent.ServiceType.values()) { - // Service key - skStats.put(type, new ConcurrentHashMap<>()); - } - - - appRtStats.addAll(initStats(OP_TYPE_REGISTER)); - appRtStats.addAll(initStats(OP_TYPE_SUBSCRIBE)); - appRtStats.addAll(initStats(OP_TYPE_NOTIFY)); - - serviceRtStats.addAll(initStats(OP_TYPE_REGISTER_SERVICE)); - serviceRtStats.addAll(initStats(OP_TYPE_SUBSCRIBE_SERVICE)); - } - - private List> initStats(String registryOpType) { - List> singleRtStats = new ArrayList<>(); - singleRtStats.add(new AtomicLongContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.METRIC_RT_LAST))); - singleRtStats.add(new LongAccumulatorContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.METRIC_RT_MIN), new LongAccumulator(Long::min, Long.MAX_VALUE))); - singleRtStats.add(new LongAccumulatorContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.METRIC_RT_MAX), new LongAccumulator(Long::max, Long.MIN_VALUE))); - singleRtStats.add(new AtomicLongContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.METRIC_RT_SUM), (responseTime, longAccumulator) -> longAccumulator.addAndGet(responseTime))); - // AvgContainer is a special counter that stores the number of times but outputs function of sum/times - AtomicLongContainer avgContainer = new AtomicLongContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.METRIC_RT_AVG), (k, v) -> v.incrementAndGet()); - avgContainer.setValueSupplier(applicationName -> { - LongContainer totalContainer = appRtStats.stream().filter(longContainer -> longContainer.isKeyWrapper(MetricsKey.METRIC_RT_SUM, registryOpType)).findFirst().get(); - AtomicLong totalRtTimes = avgContainer.get(applicationName); - AtomicLong totalRtSum = (AtomicLong) totalContainer.get(applicationName); - return totalRtSum.get() / totalRtTimes.get(); - }); - singleRtStats.add(avgContainer); - return singleRtStats; - } - - public void setApplicationKey(RegistryEvent.ApplicationType type, String applicationName, int num) { - if (!applicationNumStats.containsKey(type)) { - return; - } - applicationNumStats.get(type).computeIfAbsent(applicationName, k -> new AtomicLong(0L)).set(num); - } - - public void setServiceKey(RegistryEvent.ServiceType type, String applicationName, String serviceKey, int num) { - if (!skStats.containsKey(type)) { - return; - } - skStats.get(type).computeIfAbsent(new ServiceKeyMetric(applicationName, serviceKey), k -> new AtomicLong(0L)).set(num); - } - - public void increment(RegistryEvent.ApplicationType type, String applicationName) { - incrementSize(type, applicationName, 1); - } - - public void incrementServiceKey(RegistryEvent.ServiceType type, String applicationName, String serviceKey, int size) { - if (!skStats.containsKey(type)) { - return; - } - skStats.get(type).computeIfAbsent(new ServiceKeyMetric(applicationName, serviceKey), k -> new AtomicLong(0L)).getAndAdd(size); - } - - public void incrementSize(RegistryEvent.ApplicationType type, String applicationName, int size) { - if (!applicationNumStats.containsKey(type)) { - return; - } - applicationNumStats.get(type).computeIfAbsent(applicationName, k -> new AtomicLong(0L)).getAndAdd(size); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public void calcApplicationRt(String applicationName, String registryOpType, Long responseTime) { - for (LongContainer container : appRtStats.stream().filter(longContainer -> longContainer.specifyType(registryOpType)).collect(Collectors.toList())) { - Number current = (Number) ConcurrentHashMapUtils.computeIfAbsent(container, applicationName, container.getInitFunc()); - container.getConsumerFunc().accept(responseTime, current); - } - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public void calcServiceKeyRt(String applicationName, String serviceKey, String registryOpType, Long responseTime) { - for (LongContainer container : serviceRtStats.stream().filter(longContainer -> longContainer.specifyType(registryOpType)).collect(Collectors.toList())) { - Number current = (Number) ConcurrentHashMapUtils.computeIfAbsent(container, applicationName + "_" + serviceKey, container.getInitFunc()); - container.getConsumerFunc().accept(responseTime, current); - } - } - - @Override - @SuppressWarnings({"rawtypes"}) - public List exportNumMetrics() { - List list = new ArrayList<>(); - for (RegistryEvent.ApplicationType type : applicationNumStats.keySet()) { - Map stringAtomicLongMap = applicationNumStats.get(type); - for (String applicationName : stringAtomicLongMap.keySet()) { - list.add(convertToSample(applicationName, type, MetricsCategory.REGISTRY, stringAtomicLongMap.get(applicationName))); - } - } - return list; - } - - @Override - @SuppressWarnings({"rawtypes"}) - public List exportRtMetrics() { - List result = new ArrayList<>(); - doExportRt(result, appRtStats, ApplicationMetric::getTagsByName); - doExportRt(result, serviceRtStats, ApplicationMetric::getServiceTags); - return result; - } - - @SuppressWarnings({"rawtypes"}) - private void doExportRt(List list, List> rtStats, Function> tagNameFunc) { - for (LongContainer rtContainer : rtStats) { - MetricsKeyWrapper metricsKeyWrapper = rtContainer.getMetricsKeyWrapper(); - for (Map.Entry entry : rtContainer.entrySet()) { - list.add(new GaugeMetricSample<>(metricsKeyWrapper.targetKey(), metricsKeyWrapper.targetDesc(), tagNameFunc.apply(entry.getKey()), MetricsCategory.RT, entry.getKey().intern(), value -> rtContainer.getValueSupplier().apply(value.intern()))); - } - } - } - - @SuppressWarnings({"rawtypes"}) - public List exportSkMetrics() { - List list = new ArrayList<>(); - for (RegistryEvent.ServiceType type : skStats.keySet()) { - Map stringAtomicLongMap = skStats.get(type); - for (ServiceKeyMetric serviceKeyMetric : stringAtomicLongMap.keySet()) { - list.add(new GaugeMetricSample<>(type.getMetricsKey(), serviceKeyMetric.getTags(), MetricsCategory.REGISTRY, stringAtomicLongMap, value -> value.get(serviceKeyMetric).get())); - } - } - return list; - } - - @SuppressWarnings({"rawtypes"}) - public GaugeMetricSample convertToSample(String applicationName, RegistryEvent.ApplicationType type, MetricsCategory category, AtomicLong targetNumber) { - return new GaugeMetricSample<>(type.getMetricsKey(), ApplicationMetric.getTagsByName(applicationName), category, targetNumber, AtomicLong::get); - } -} diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsNotifyListener.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsNotifyListener.java deleted file mode 100644 index 6dd7828faf..0000000000 --- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsNotifyListener.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.dubbo.metrics.registry.event; - -import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; -import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.metrics.event.MetricsEvent; -import org.apache.dubbo.metrics.listener.MetricsLifeListener; - -import static org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_NOTIFY; - -/** - * The registration center actively pushes Listener, no failure and rt statistics - */ -public class MetricsNotifyListener implements MetricsLifeListener { - - protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); - - @Override - public boolean isSupport(MetricsEvent event) { - return event instanceof RegistryEvent.MetricsNotifyEvent; - } - - @Override - public void onEvent(RegistryEvent.MetricsNotifyEvent event) { - event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.ApplicationType.N_TOTAL); - } - - @Override - public void onEventFinish(RegistryEvent.MetricsNotifyEvent event) { - event.getCollector().setNum(RegistryEvent.ServiceType.N_LAST_NUM, event.getSource().getApplicationName(), event.getLastNotifyNum()); - event.getCollector().addApplicationRT(event.getSource().getApplicationName(), OP_TYPE_NOTIFY, event.getTimePair().calc()); - } - - @Override - public void onEventError(RegistryEvent.MetricsNotifyEvent event) { - } - -} diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsRegisterListener.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsRegisterListener.java deleted file mode 100644 index 8fbe835a37..0000000000 --- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsRegisterListener.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.dubbo.metrics.registry.event; - -import org.apache.dubbo.metrics.event.MetricsEvent; -import org.apache.dubbo.metrics.listener.MetricsLifeListener; - -import static org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_REGISTER; - -public class MetricsRegisterListener implements MetricsLifeListener { - - - @Override - public boolean isSupport(MetricsEvent event) { - return event instanceof RegistryEvent.MetricsApplicationRegisterEvent; - } - - @Override - public void onEvent(RegistryEvent.MetricsApplicationRegisterEvent event) { - if (!event.isAvailable()) { - return; - } - event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.ApplicationType.R_TOTAL); - } - - @Override - public void onEventFinish(RegistryEvent.MetricsApplicationRegisterEvent event) { - event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.ApplicationType.R_SUCCEED); - event.getCollector().addApplicationRT(event.getSource().getApplicationName(), OP_TYPE_REGISTER, event.getTimePair().calc()); - } - - @Override - public void onEventError(RegistryEvent.MetricsApplicationRegisterEvent event) { - event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.ApplicationType.R_FAILED); - event.getCollector().addApplicationRT(event.getSource().getApplicationName(), OP_TYPE_REGISTER, event.getTimePair().calc()); - } -} diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsServiceRegisterListener.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsServiceRegisterListener.java deleted file mode 100644 index e99ce7a75d..0000000000 --- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsServiceRegisterListener.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.dubbo.metrics.registry.event; - -import org.apache.dubbo.metrics.event.MetricsEvent; -import org.apache.dubbo.metrics.listener.MetricsLifeListener; - -import static org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_REGISTER_SERVICE; - -public class MetricsServiceRegisterListener implements MetricsLifeListener { - - @Override - public boolean isSupport(MetricsEvent event) { - return event instanceof RegistryEvent.MetricsServiceRegisterEvent; - } - - @Override - public void onEvent(RegistryEvent.MetricsServiceRegisterEvent event) { - if (!event.isAvailable()) { - return; - } - event.getCollector().incrementServiceKey(event.getSource().getApplicationName(), event.getServiceKey(), RegistryEvent.ServiceType.R_SERVICE_TOTAL, event.getSize()); - } - - @Override - public void onEventFinish(RegistryEvent.MetricsServiceRegisterEvent event) { - event.getCollector().incrementServiceKey(event.getSource().getApplicationName(), event.getServiceKey(), RegistryEvent.ServiceType.R_SERVICE_SUCCEED, event.getSize()); - event.getCollector().addServiceKeyRT(event.getSource().getApplicationName(), event.getServiceKey(), OP_TYPE_REGISTER_SERVICE, event.getTimePair().calc()); - } - - @Override - public void onEventError(RegistryEvent.MetricsServiceRegisterEvent event) { - event.getCollector().incrementServiceKey(event.getSource().getApplicationName(), event.getServiceKey(), RegistryEvent.ServiceType.R_SERVICE_FAILED, event.getSize()); - event.getCollector().addApplicationRT(event.getSource().getApplicationName(), OP_TYPE_REGISTER_SERVICE, event.getTimePair().calc()); - } -} diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsServiceSubscribeListener.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsServiceSubscribeListener.java deleted file mode 100644 index 02062016dd..0000000000 --- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsServiceSubscribeListener.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.dubbo.metrics.registry.event; - -import org.apache.dubbo.metrics.event.MetricsEvent; -import org.apache.dubbo.metrics.listener.MetricsLifeListener; - -import static org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_SUBSCRIBE_SERVICE; - -public class MetricsServiceSubscribeListener implements MetricsLifeListener { - - @Override - public boolean isSupport(MetricsEvent event) { - return event instanceof RegistryEvent.MetricsServiceSubscribeEvent; - } - - @Override - public void onEvent(RegistryEvent.MetricsServiceSubscribeEvent event) { - if (!event.isAvailable()) { - return; - } - event.getCollector().incrementServiceKey(event.getSource().getApplicationName(), event.getUniqueServiceName(), RegistryEvent.ServiceType.S_SERVICE_TOTAL, 1); - } - - @Override - public void onEventFinish(RegistryEvent.MetricsServiceSubscribeEvent event) { - event.getCollector().incrementServiceKey(event.getSource().getApplicationName(), event.getUniqueServiceName(), RegistryEvent.ServiceType.S_SERVICE_SUCCEED, 1); - event.getCollector().addApplicationRT(event.getSource().getApplicationName(), OP_TYPE_SUBSCRIBE_SERVICE, event.getTimePair().calc()); - } - - @Override - public void onEventError(RegistryEvent.MetricsServiceSubscribeEvent event) { - event.getCollector().incrementServiceKey(event.getSource().getApplicationName(), event.getUniqueServiceName(), RegistryEvent.ServiceType.S_SERVICE_FAILED, 1); - event.getCollector().addApplicationRT(event.getSource().getApplicationName(), OP_TYPE_SUBSCRIBE_SERVICE, event.getTimePair().calc()); - } - -} diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsSubscribeListener.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsSubscribeListener.java deleted file mode 100644 index 1caf41c489..0000000000 --- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsSubscribeListener.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.dubbo.metrics.registry.event; - -import org.apache.dubbo.metrics.event.MetricsEvent; -import org.apache.dubbo.metrics.listener.MetricsLifeListener; - -import static org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_SUBSCRIBE; - -public class MetricsSubscribeListener implements MetricsLifeListener { - - @Override - public boolean isSupport(MetricsEvent event) { - return event instanceof RegistryEvent.MetricsSubscribeEvent; - } - - @Override - public void onEvent(RegistryEvent.MetricsSubscribeEvent event) { - if (!event.isAvailable()) { - return; - } - event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.ApplicationType.S_TOTAL); - } - - @Override - public void onEventFinish(RegistryEvent.MetricsSubscribeEvent event) { - event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.ApplicationType.S_SUCCEED); - event.getCollector().addApplicationRT(event.getSource().getApplicationName(), OP_TYPE_SUBSCRIBE, event.getTimePair().calc()); - } - - @Override - public void onEventError(RegistryEvent.MetricsSubscribeEvent event) { - event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.ApplicationType.S_FAILED); - event.getCollector().addApplicationRT(event.getSource().getApplicationName(), OP_TYPE_SUBSCRIBE, event.getTimePair().calc()); - } - -} diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryEvent.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryEvent.java index 5fb27ab31d..e48493a957 100644 --- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryEvent.java +++ b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryEvent.java @@ -17,207 +17,132 @@ package org.apache.dubbo.metrics.registry.event; -import org.apache.dubbo.metrics.event.MetricsEvent; -import org.apache.dubbo.metrics.event.TimeCounter; -import org.apache.dubbo.metrics.model.MetricsKey; -import org.apache.dubbo.metrics.model.TimePair; +import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; +import org.apache.dubbo.metrics.event.TimeCounterEvent; +import org.apache.dubbo.metrics.exception.MetricsNeverHappenException; +import org.apache.dubbo.metrics.model.key.MetricsLevel; +import org.apache.dubbo.metrics.model.key.TypeWrapper; import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector; +import org.apache.dubbo.metrics.registry.event.type.ApplicationType; +import org.apache.dubbo.metrics.registry.event.type.ServiceType; import org.apache.dubbo.rpc.model.ApplicationModel; +import java.util.HashMap; import java.util.Map; +import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_DIRECTORY_MAP; +import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_LAST_NUM_MAP; +import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SERVICE; +import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SIZE; + + /** * Registry related events */ -public class RegistryEvent extends MetricsEvent implements TimeCounter { - private final TimePair timePair; +public class RegistryEvent extends TimeCounterEvent { private final RegistryMetricsCollector collector; - private final boolean available; + private final Map attachment = new HashMap<>(8); - public RegistryEvent(ApplicationModel applicationModel, TimePair timePair) { + public RegistryEvent(ApplicationModel applicationModel, TypeWrapper typeWrapper) { super(applicationModel); - this.timePair = timePair; - this.collector = applicationModel.getBeanFactory().getBean(RegistryMetricsCollector.class); - this.available = this.collector != null && collector.isCollectEnabled(); + super.typeWrapper = typeWrapper; + ScopeBeanFactory beanFactory = getSource().getBeanFactory(); + if (beanFactory.isDestroyed()) { + this.collector = null; + } else { + this.collector = beanFactory.getBean(RegistryMetricsCollector.class); + super.setAvailable(this.collector != null && collector.isCollectEnabled()); + } } + public ApplicationModel getSource() { - return (ApplicationModel) source; + return source; } public RegistryMetricsCollector getCollector() { return collector; } - public boolean isAvailable() { - return available; + @SuppressWarnings("unchecked") + public T getAttachmentValue(String key) { + if (!attachment.containsKey(key)) { + throw new MetricsNeverHappenException("Attachment key [" + key + "] not found"); + } + return (T) attachment.get(key); } - @Override - public TimePair getTimePair() { - return timePair; + public void putAttachment(String key, Object value) { + attachment.put(key, value); } - public enum ApplicationType { - R_TOTAL(MetricsKey.REGISTER_METRIC_REQUESTS), - R_SUCCEED(MetricsKey.REGISTER_METRIC_REQUESTS_SUCCEED), - R_FAILED(MetricsKey.REGISTER_METRIC_REQUESTS_FAILED), - - S_TOTAL(MetricsKey.SUBSCRIBE_METRIC_NUM), - S_SUCCEED(MetricsKey.SUBSCRIBE_METRIC_NUM_SUCCEED), - S_FAILED(MetricsKey.SUBSCRIBE_METRIC_NUM_FAILED), - - D_VALID(MetricsKey.DIRECTORY_METRIC_NUM_VALID), - D_UN_VALID(MetricsKey.DIRECTORY_METRIC_NUM_UN_VALID), - D_DISABLE(MetricsKey.DIRECTORY_METRIC_NUM_DISABLE), - D_CURRENT(MetricsKey.DIRECTORY_METRIC_NUM_CURRENT, false), - D_RECOVER_DISABLE(MetricsKey.DIRECTORY_METRIC_NUM_RECOVER_DISABLE), - - N_TOTAL(MetricsKey.NOTIFY_METRIC_REQUESTS), - ; - - private final MetricsKey metricsKey; - private final boolean isIncrement; - - - ApplicationType(MetricsKey metricsKey) { - this(metricsKey, true); - } - - ApplicationType(MetricsKey metricsKey, boolean isIncrement) { - this.metricsKey = metricsKey; - this.isIncrement = isIncrement; - } - - public MetricsKey getMetricsKey() { - return metricsKey; - } - - public boolean isIncrement() { - return isIncrement; - } + public void setLastNum(ServiceType type) { + getCollector().setNum(type, getSource().getApplicationName(), getAttachmentValue(ATTACHMENT_KEY_LAST_NUM_MAP)); } - public enum ServiceType { - - N_LAST_NUM(MetricsKey.NOTIFY_METRIC_NUM_LAST), - - R_SERVICE_TOTAL(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS), - R_SERVICE_SUCCEED(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED), - R_SERVICE_FAILED(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_FAILED), - - S_SERVICE_TOTAL(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM), - S_SERVICE_SUCCEED(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_SUCCEED), - S_SERVICE_FAILED(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_FAILED), - ; - - private final MetricsKey metricsKey; - private final boolean isIncrement; - - - ServiceType(MetricsKey metricsKey) { - this(metricsKey, true); - } - - ServiceType(MetricsKey metricsKey, boolean isIncrement) { - this.metricsKey = metricsKey; - this.isIncrement = isIncrement; - } - - public MetricsKey getMetricsKey() { - return metricsKey; - } - - public boolean isIncrement() { - return isIncrement; - } - } - - public static class MetricsApplicationRegisterEvent extends RegistryEvent { - - public MetricsApplicationRegisterEvent(ApplicationModel applicationModel, TimePair timePair) { - super(applicationModel, timePair); - } + public void addApplicationRT(String opType) { + getCollector().addApplicationRT(getSource().getApplicationName(), opType, getTimePair().calc()); } - public static class MetricsSubscribeEvent extends RegistryEvent { - - public MetricsSubscribeEvent(ApplicationModel applicationModel, TimePair timePair) { - super(applicationModel, timePair); - } - + public void setNum(ApplicationType type, String attachmentKey) { + getCollector().setNum(type, getSource().getApplicationName(), getAttachmentValue(attachmentKey)); } - public static class MetricsNotifyEvent extends RegistryEvent { - - private final Map lastNumMap; - - public MetricsNotifyEvent(ApplicationModel applicationModel, TimePair timePair, Map lastNumMap) { - super(applicationModel, timePair); - this.lastNumMap = lastNumMap; - } - - public Map getLastNotifyNum() { - return lastNumMap; - } + public void incrementServiceKey(ServiceType type, String attServiceKey, String attSize) { + incrementServiceKey(type, attServiceKey, (int) getAttachmentValue(attSize)); } - public static class MetricsDirectoryEvent extends RegistryEvent { - - private final ApplicationType type; - private final int size; - - public MetricsDirectoryEvent(ApplicationModel applicationModel, ApplicationType type) { - this(applicationModel, type, 1); - } - - public MetricsDirectoryEvent(ApplicationModel applicationModel, ApplicationType type, int size) { - super(applicationModel, TimePair.empty()); - this.type = type; - this.size = size; - } - - public ApplicationType getType() { - return type; - } - - public int getSize() { - return size; - } + public void incrementServiceKey(ServiceType type, String attServiceKey, int size) { + getCollector().incrementServiceKey(getSource().getApplicationName(), getAttachmentValue(attServiceKey), type, size); } - public static class MetricsServiceRegisterEvent extends RegistryEvent { - - private final int size; - private final String serviceKey; - - public MetricsServiceRegisterEvent(ApplicationModel applicationModel, TimePair timePair, String serviceKey, int size) { - super(applicationModel, timePair); - this.size = size; - this.serviceKey = serviceKey; - } - - public int getSize() { - return size; - } - - public String getServiceKey() { - return serviceKey; - } + public void addServiceKeyRT(String attServiceKey, String attSize) { + getCollector().addServiceKeyRT(getSource().getApplicationName(), getAttachmentValue(attServiceKey), attSize, getTimePair().calc()); } - public static class MetricsServiceSubscribeEvent extends RegistryEvent { - - private final String uniqueServiceName; - - public MetricsServiceSubscribeEvent(ApplicationModel applicationModel, TimePair timePair, String uniqueServiceName) { - super(applicationModel, timePair); - this.uniqueServiceName = uniqueServiceName; - } - - public String getUniqueServiceName() { - return uniqueServiceName; - } + public void increment(ApplicationType type) { + getCollector().increment(getSource().getApplicationName(), type); } + + + public static RegistryEvent toRegisterEvent(ApplicationModel applicationModel) { + return new RegistryEvent(applicationModel, new TypeWrapper(MetricsLevel.APP, ApplicationType.R_TOTAL, ApplicationType.R_SUCCEED, ApplicationType.R_FAILED)); + } + + + public static RegistryEvent toSubscribeEvent(ApplicationModel applicationModel) { + return new RegistryEvent(applicationModel, new TypeWrapper(MetricsLevel.APP, ApplicationType.S_TOTAL, ApplicationType.S_SUCCEED, ApplicationType.S_FAILED)); + } + + + public static RegistryEvent toNotifyEvent(ApplicationModel applicationModel) { + return new RegistryEvent(applicationModel, new TypeWrapper(MetricsLevel.APP, ApplicationType.N_TOTAL, ServiceType.N_LAST_NUM, null)) { + @Override + public void customAfterPost(Object postResult) { + super.putAttachment(ATTACHMENT_KEY_LAST_NUM_MAP, postResult); + } + }; + } + + public static RegistryEvent toRsEvent(ApplicationModel applicationModel, String serviceKey, int size) { + RegistryEvent ddEvent = new RegistryEvent(applicationModel, new TypeWrapper(MetricsLevel.SERVICE, ServiceType.R_SERVICE_TOTAL, ServiceType.R_SERVICE_SUCCEED, ServiceType.R_SERVICE_FAILED)); + ddEvent.putAttachment(ATTACHMENT_KEY_SERVICE, serviceKey); + ddEvent.putAttachment(ATTACHMENT_KEY_SIZE, size); + return ddEvent; + } + + public static RegistryEvent toSsEvent(ApplicationModel applicationModel, String serviceKey) { + RegistryEvent ddEvent = new RegistryEvent(applicationModel, new TypeWrapper(MetricsLevel.SERVICE, ServiceType.S_SERVICE_TOTAL, ServiceType.S_SERVICE_SUCCEED, ServiceType.S_SERVICE_FAILED)); + ddEvent.putAttachment(ATTACHMENT_KEY_SERVICE, serviceKey); + return ddEvent; + } + + public static RegistryEvent refreshDirectoryEvent(ApplicationModel applicationModel, Map> summaryMap) { + RegistryEvent registryEvent = new RegistryEvent(applicationModel, new TypeWrapper(MetricsLevel.APP, ServiceType.D_VALID, null, null)); + registryEvent.putAttachment(ATTACHMENT_DIRECTORY_MAP, summaryMap); + return registryEvent; + } + + } diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryListener.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryListener.java new file mode 100644 index 0000000000..beff249b1c --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryListener.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.registry.event; + +import org.apache.dubbo.metrics.event.MetricsEvent; +import org.apache.dubbo.metrics.listener.MetricsLifeListener; + +import java.util.function.BiConsumer; + +public abstract class RegistryListener implements MetricsLifeListener { + + private final Object enumType; + + public RegistryListener(Object enumType) { + this.enumType = enumType; + } + + @Override + public boolean isSupport(MetricsEvent event) { + return event.isAvailable() && event.isAssignableFrom(enumType); + } + + static RegistryListener onEvent(T enumType, BiConsumer postFunc) { + + return new RegistryListener(enumType) { + @Override + public void onEvent(RegistryEvent event) { + postFunc.accept(event, enumType); + } + }; + } + + static RegistryListener onFinish(T enumType, BiConsumer finishFunc) { + + return new RegistryListener(enumType) { + @Override + public void onEventFinish(RegistryEvent event) { + finishFunc.accept(event, enumType); + } + }; + } + + static RegistryListener onError(T enumType, BiConsumer errorFunc) { + + return new RegistryListener(enumType) { + @Override + public void onEventError(RegistryEvent event) { + errorFunc.accept(event, enumType); + } + }; + } +} diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryMetricsEventMulticaster.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryMetricsEventMulticaster.java index 3ad78a26c8..e576c35339 100644 --- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryMetricsEventMulticaster.java +++ b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryMetricsEventMulticaster.java @@ -18,18 +18,107 @@ package org.apache.dubbo.metrics.registry.event; import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster; +import org.apache.dubbo.metrics.registry.event.type.ApplicationType; +import org.apache.dubbo.metrics.registry.event.type.ServiceType; + +import java.util.Map; + +import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_DIRECTORY_MAP; +import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SERVICE; +import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SIZE; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_NOTIFY; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER_SERVICE; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE_SERVICE; public final class RegistryMetricsEventMulticaster extends SimpleMetricsEventMulticaster { public RegistryMetricsEventMulticaster() { - super.addListener(new MetricsRegisterListener()); - super.addListener(new MetricsSubscribeListener()); - super.addListener(new MetricsNotifyListener()); - super.addListener(new MetricsDirectoryListener()); - super.addListener(new MetricsServiceRegisterListener()); - super.addListener(new MetricsServiceSubscribeListener()); + // MetricsRegisterListener + super.addListener(onPostEventBuild(ApplicationType.R_TOTAL)); + super.addListener(onFinishEventBuild(ApplicationType.R_SUCCEED, OP_TYPE_REGISTER.getType())); + super.addListener(onErrorEventBuild(ApplicationType.R_FAILED, OP_TYPE_REGISTER.getType())); - setAvailable(); + // MetricsSubscribeListener + super.addListener(onPostEventBuild(ApplicationType.S_TOTAL)); + super.addListener(onFinishEventBuild(ApplicationType.S_SUCCEED, OP_TYPE_SUBSCRIBE.getType())); + super.addListener(onErrorEventBuild(ApplicationType.S_FAILED, OP_TYPE_SUBSCRIBE.getType())); + + // MetricsNotifyListener + super.addListener(onPostEventBuild(ApplicationType.N_TOTAL)); + super.addListener( + RegistryListener.onFinish(ServiceType.N_LAST_NUM, + (event, type) -> { + event.setLastNum(type); + event.addApplicationRT(OP_TYPE_NOTIFY.getType()); + } + )); + + + // MetricsDirectoryListener + super.addListener(RegistryListener.onEvent(ServiceType.D_VALID, + (event, type) -> + { + Map> summaryMap = event.getAttachmentValue(ATTACHMENT_DIRECTORY_MAP); + summaryMap.forEach((serviceType, map) -> + event.getCollector().setNum(serviceType, event.getSource().getApplicationName(), map)); + } + )); + + // MetricsServiceRegisterListener + super.addListener(RegistryListener.onEvent(ServiceType.R_SERVICE_TOTAL, + this::incrSkSize + )); + super.addListener(RegistryListener.onFinish(ServiceType.R_SERVICE_SUCCEED, this::onRegisterRtEvent)); + super.addListener(RegistryListener.onError(ServiceType.R_SERVICE_FAILED, this::onRegisterRtEvent)); + + // MetricsServiceSubscribeListener + super.addListener(RegistryListener.onEvent(ServiceType.S_SERVICE_TOTAL, this::incrSk)); + super.addListener(RegistryListener.onFinish(ServiceType.S_SERVICE_SUCCEED, this::onRtEvent)); + super.addListener(RegistryListener.onError(ServiceType.S_SERVICE_FAILED, this::onRtEvent)); } + private RegistryListener onPostEventBuild(ApplicationType applicationType) { + return RegistryListener.onEvent(applicationType, + (event, type) -> event.getCollector().increment(event.getSource().getApplicationName(), type) + ); + } + + private RegistryListener onFinishEventBuild(ApplicationType applicationType, String registryOpType) { + return RegistryListener.onFinish(applicationType, + (event, type) -> { + event.increment(type); + event.addApplicationRT(registryOpType); + } + ); + } + + private RegistryListener onErrorEventBuild(ApplicationType applicationType, String registryOpType) { + return RegistryListener.onError(applicationType, + (event, type) -> { + event.increment(type); + event.addApplicationRT(registryOpType); + } + ); + } + + + private void incrSk(RegistryEvent event, ServiceType type) { + event.incrementServiceKey(type, ATTACHMENT_KEY_SERVICE, 1); + } + + private void incrSkSize(RegistryEvent event, ServiceType type) { + event.incrementServiceKey(type, ATTACHMENT_KEY_SERVICE, ATTACHMENT_KEY_SIZE); + } + + private void onRtEvent(RegistryEvent event, ServiceType type) { + incrSk(event, type); + event.addServiceKeyRT(ATTACHMENT_KEY_SERVICE, OP_TYPE_SUBSCRIBE_SERVICE.getType()); + } + + private void onRegisterRtEvent(RegistryEvent event, ServiceType type) { + incrSkSize(event, type); + event.addServiceKeyRT(ATTACHMENT_KEY_SERVICE, OP_TYPE_REGISTER_SERVICE.getType()); + } } diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/type/ApplicationType.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/type/ApplicationType.java new file mode 100644 index 0000000000..1827c13554 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/type/ApplicationType.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.registry.event.type; + +import org.apache.dubbo.metrics.model.key.MetricsKey; + +public enum ApplicationType { + R_TOTAL(MetricsKey.REGISTER_METRIC_REQUESTS), + R_SUCCEED(MetricsKey.REGISTER_METRIC_REQUESTS_SUCCEED), + R_FAILED(MetricsKey.REGISTER_METRIC_REQUESTS_FAILED), + + S_TOTAL(MetricsKey.SUBSCRIBE_METRIC_NUM), + S_SUCCEED(MetricsKey.SUBSCRIBE_METRIC_NUM_SUCCEED), + S_FAILED(MetricsKey.SUBSCRIBE_METRIC_NUM_FAILED), + + N_TOTAL(MetricsKey.NOTIFY_METRIC_REQUESTS), + ; + + private final MetricsKey metricsKey; + + + ApplicationType(MetricsKey metricsKey) { + this.metricsKey = metricsKey; + } + + public MetricsKey getMetricsKey() { + return metricsKey; + } + +} diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/type/ServiceType.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/type/ServiceType.java new file mode 100644 index 0000000000..eda455087a --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/type/ServiceType.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.metrics.registry.event.type; + +import org.apache.dubbo.metrics.model.key.MetricsKey; + +public enum ServiceType { + + N_LAST_NUM(MetricsKey.NOTIFY_METRIC_NUM_LAST), + + R_SERVICE_TOTAL(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS), + R_SERVICE_SUCCEED(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED), + R_SERVICE_FAILED(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_FAILED), + + S_SERVICE_TOTAL(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM), + S_SERVICE_SUCCEED(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_SUCCEED), + S_SERVICE_FAILED(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_FAILED), + + D_VALID(MetricsKey.DIRECTORY_METRIC_NUM_VALID), + D_TO_RECONNECT(MetricsKey.DIRECTORY_METRIC_NUM_TO_RECONNECT), + D_DISABLE(MetricsKey.DIRECTORY_METRIC_NUM_DISABLE), + D_ALL(MetricsKey.DIRECTORY_METRIC_NUM_ALL), + ; + + private final MetricsKey metricsKey; + + ServiceType(MetricsKey metricsKey) { + this.metricsKey = metricsKey; + } + + public MetricsKey getMetricsKey() { + return metricsKey; + } + +} diff --git a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsCollectorTest.java b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsCollectorTest.java index 7b78456f2b..7c3012795b 100644 --- a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsCollectorTest.java +++ b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsCollectorTest.java @@ -18,9 +18,10 @@ package org.apache.dubbo.metrics.registry.metrics.collector; import org.apache.dubbo.config.ApplicationConfig; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; -import org.apache.dubbo.metrics.model.MetricsKey; -import org.apache.dubbo.metrics.model.MetricsKeyWrapper; +import org.apache.dubbo.metrics.event.MetricsDispatcher; +import org.apache.dubbo.metrics.event.MetricsEventBus; +import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; +import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.TimePair; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; @@ -35,10 +36,13 @@ import org.junit.jupiter.api.Test; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; -import static org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_REGISTER; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER_SERVICE; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE_SERVICE; class RegistryMetricsCollectorTest { @@ -62,32 +66,45 @@ class RegistryMetricsCollectorTest { } @Test - void testPushMetrics() throws InterruptedException { + void testRegisterMetrics() { - TimePair timePair = TimePair.start(); - GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getOrRegisterBean(GlobalMetricsEventMulticaster.class); + applicationModel.getBeanFactory().getOrRegisterBean(MetricsDispatcher.class); RegistryMetricsCollector collector = applicationModel.getBeanFactory().getOrRegisterBean(RegistryMetricsCollector.class); collector.setCollectEnabled(true); - eventMulticaster.publishEvent(new RegistryEvent.MetricsApplicationRegisterEvent(applicationModel, timePair)); - List metricSamples = collector.collect(); + RegistryEvent registryEvent = RegistryEvent.toRegisterEvent(applicationModel); + MetricsEventBus.post(registryEvent, + () -> { + List metricSamples = collector.collect(); + // push success +1 + Assertions.assertEquals(1, metricSamples.size()); + Assertions.assertTrue(metricSamples.get(0) instanceof GaugeMetricSample); + return null; + } + ); - // push success +1 - Assertions.assertEquals(1, metricSamples.size()); - Assertions.assertTrue(metricSamples.get(0) instanceof GaugeMetricSample); - - eventMulticaster.publishFinishEvent(new RegistryEvent.MetricsApplicationRegisterEvent(applicationModel, timePair)); // push finish rt +1 - metricSamples = collector.collect(); + List metricSamples = collector.collect(); //num(total+success) + rt(5) = 7 Assertions.assertEquals(7, metricSamples.size()); - long c1 = timePair.calc(); - TimePair lastTimePair = TimePair.start(); - eventMulticaster.publishEvent(new RegistryEvent.MetricsApplicationRegisterEvent(applicationModel, lastTimePair)); - Thread.sleep(50); + long c1 = registryEvent.getTimePair().calc(); + + + registryEvent = RegistryEvent.toRegisterEvent(applicationModel); + TimePair lastTimePair = registryEvent.getTimePair(); + MetricsEventBus.post(registryEvent, + () -> { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }, Objects::nonNull + ); // push error rt +1 - eventMulticaster.publishErrorEvent(new RegistryEvent.MetricsApplicationRegisterEvent(applicationModel, lastTimePair)); long c2 = lastTimePair.calc(); + metricSamples = collector.collect(); // num(total+success+error) + rt(5) @@ -102,12 +119,140 @@ class RegistryMetricsCollectorTest { @SuppressWarnings("rawtypes") Map sampleMap = metricSamples.stream().collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_REGISTER, MetricsKey.METRIC_RT_LAST).targetKey()), lastTimePair.calc()); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_REGISTER, MetricsKey.METRIC_RT_MIN).targetKey()), Math.min(c1, c2)); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_REGISTER, MetricsKey.METRIC_RT_MAX).targetKey()), Math.max(c1, c2)); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_REGISTER, MetricsKey.METRIC_RT_AVG).targetKey()), (c1 + c2) / 2); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_REGISTER, MetricsKey.METRIC_RT_SUM).targetKey()), c1 + c2); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_REGISTER).targetKey()), lastTimePair.calc()); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_REGISTER).targetKey()), Math.min(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_REGISTER).targetKey()), Math.max(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_REGISTER).targetKey()), (c1 + c2) / 2); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_REGISTER).targetKey()), c1 + c2); } + @Test + void testServicePushMetrics() { + + applicationModel.getBeanFactory().getOrRegisterBean(MetricsDispatcher.class); + RegistryMetricsCollector collector = applicationModel.getBeanFactory().getOrRegisterBean(RegistryMetricsCollector.class); + collector.setCollectEnabled(true); + String serviceName = "demo.gameService"; + + RegistryEvent registryEvent = RegistryEvent.toRsEvent(applicationModel, serviceName, 2); + MetricsEventBus.post(registryEvent, + () -> { + List metricSamples = collector.collect(); + + // push success +1 + Assertions.assertEquals(1, metricSamples.size()); + Assertions.assertTrue(metricSamples.get(0) instanceof GaugeMetricSample); + Assertions.assertEquals(metricSamples.get(0).getName(), MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS.getName()); + Assertions.assertEquals(metricSamples.get(0).getTags().get("interface"), serviceName); + return null; + } + ); + + // push finish rt +1 + List metricSamples = collector.collect(); + //num(total+success) + rt(5) = 7 + Assertions.assertEquals(7, metricSamples.size()); + + long c1 = registryEvent.getTimePair().calc(); + registryEvent = RegistryEvent.toRsEvent(applicationModel, serviceName, 2); + TimePair lastTimePair = registryEvent.getTimePair(); + MetricsEventBus.post(registryEvent, + () -> { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }, Objects::nonNull + ); + // push error rt +1 + long c2 = lastTimePair.calc(); + + metricSamples = collector.collect(); + + // num(total+success+error) + rt(5) + Assertions.assertEquals(8, metricSamples.size()); + + // calc rt + for (MetricSample sample : metricSamples) { + Map tags = sample.getTags(); + Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); + } + + @SuppressWarnings("rawtypes") + Map sampleMap = metricSamples.stream().collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); + + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_REGISTER_SERVICE).targetKey()), lastTimePair.calc()); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_REGISTER_SERVICE).targetKey()), Math.min(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_REGISTER_SERVICE).targetKey()), Math.max(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_REGISTER_SERVICE).targetKey()), (c1 + c2) / 2); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_REGISTER_SERVICE).targetKey()), c1 + c2); + } + + + @Test + void testServiceSubscribeMetrics() { + + applicationModel.getBeanFactory().getOrRegisterBean(MetricsDispatcher.class); + RegistryMetricsCollector collector = applicationModel.getBeanFactory().getOrRegisterBean(RegistryMetricsCollector.class); + collector.setCollectEnabled(true); + String serviceName = "demo.gameService"; + + RegistryEvent subscribeEvent = RegistryEvent.toSsEvent(applicationModel, serviceName); + MetricsEventBus.post(subscribeEvent, + () -> { + List metricSamples = collector.collect(); + + // push success +1 + Assertions.assertEquals(1, metricSamples.size()); + Assertions.assertTrue(metricSamples.get(0) instanceof GaugeMetricSample); + Assertions.assertEquals(metricSamples.get(0).getName(), MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM.getName()); + Assertions.assertEquals(metricSamples.get(0).getTags().get("interface"), serviceName); + return null; + } + ); + + // push finish rt +1 + List metricSamples = collector.collect(); + //num(total+success) + rt(5) = 7 + Assertions.assertEquals(7, metricSamples.size()); + + long c1 = subscribeEvent.getTimePair().calc(); + subscribeEvent = RegistryEvent.toSsEvent(applicationModel, serviceName); + TimePair lastTimePair = subscribeEvent.getTimePair(); + MetricsEventBus.post(subscribeEvent, + () -> { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }, Objects::nonNull + ); + // push error rt +1 + long c2 = lastTimePair.calc(); + + metricSamples = collector.collect(); + + // num(total+success+error) + rt(5) + Assertions.assertEquals(8, metricSamples.size()); + + // calc rt + for (MetricSample sample : metricSamples) { + Map tags = sample.getTags(); + Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); + } + + @SuppressWarnings("rawtypes") + Map sampleMap = metricSamples.stream().collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); + + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_SUBSCRIBE_SERVICE).targetKey()), lastTimePair.calc()); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_SUBSCRIBE_SERVICE).targetKey()), Math.min(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_SUBSCRIBE_SERVICE).targetKey()), Math.max(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_SUBSCRIBE_SERVICE).targetKey()), (c1 + c2) / 2); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_SUBSCRIBE_SERVICE).targetKey()), c1 + c2); + } } diff --git a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsSampleTest.java b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsSampleTest.java index 27b7a4f736..354822f270 100644 --- a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsSampleTest.java +++ b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsSampleTest.java @@ -18,13 +18,12 @@ package org.apache.dubbo.metrics.registry.metrics.collector; import org.apache.dubbo.config.ApplicationConfig; -import org.apache.dubbo.metrics.model.MetricsKey; -import org.apache.dubbo.metrics.model.MetricsKeyWrapper; +import org.apache.dubbo.metrics.model.key.MetricsKey; +import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector; -import org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite; -import org.apache.dubbo.metrics.registry.event.RegistryEvent; +import org.apache.dubbo.metrics.registry.event.type.ApplicationType; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.AfterEach; @@ -37,6 +36,7 @@ import java.util.Map; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER; class RegistryMetricsSampleTest { @@ -65,8 +65,8 @@ class RegistryMetricsSampleTest { RegistryMetricsCollector collector = new RegistryMetricsCollector(applicationModel); collector.setCollectEnabled(true); String applicationName = applicationModel.getApplicationName(); - collector.addApplicationRT(applicationName, RegistryStatComposite.OP_TYPE_REGISTER, 10L); - collector.addApplicationRT(applicationName, RegistryStatComposite.OP_TYPE_REGISTER, 0L); + collector.addApplicationRT(applicationName, OP_TYPE_REGISTER.getType(), 10L); + collector.addApplicationRT(applicationName, OP_TYPE_REGISTER.getType(), 0L); List samples = collector.collect(); for (MetricSample sample : samples) { @@ -77,11 +77,11 @@ class RegistryMetricsSampleTest { @SuppressWarnings("rawtypes") Map sampleMap = samples.stream().collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, MetricsKey.METRIC_RT_LAST).targetKey()), 0L); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, MetricsKey.METRIC_RT_MIN).targetKey()), 0L); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, MetricsKey.METRIC_RT_MAX).targetKey()), 10L); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, MetricsKey.METRIC_RT_AVG).targetKey()), 5L); - Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, MetricsKey.METRIC_RT_SUM).targetKey()), 10L); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_REGISTER).targetKey()), 0L); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_REGISTER).targetKey()), 0L); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_REGISTER).targetKey()), 10L); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_REGISTER).targetKey()), 5L); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_REGISTER).targetKey()), 10L); } @Test @@ -89,7 +89,7 @@ class RegistryMetricsSampleTest { RegistryMetricsCollector collector = new RegistryMetricsCollector(applicationModel); collector.setCollectEnabled(true); String applicationName = applicationModel.getApplicationName(); - collector.increment(applicationName, RegistryEvent.ApplicationType.R_TOTAL); + collector.increment(applicationName, ApplicationType.R_TOTAL); } } diff --git a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryStatCompositeTest.java b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryStatCompositeTest.java index 7d4753aa1b..1016020887 100644 --- a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryStatCompositeTest.java +++ b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryStatCompositeTest.java @@ -17,9 +17,13 @@ package org.apache.dubbo.metrics.registry.metrics.collector; +import org.apache.dubbo.metrics.data.ApplicationStatComposite; +import org.apache.dubbo.metrics.data.BaseStatComposite; +import org.apache.dubbo.metrics.data.RtStatComposite; +import org.apache.dubbo.metrics.data.ServiceStatComposite; import org.apache.dubbo.metrics.model.container.LongContainer; -import org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite; -import org.apache.dubbo.metrics.registry.event.RegistryEvent; +import org.apache.dubbo.metrics.registry.RegistryMetricsConstants; +import org.apache.dubbo.metrics.registry.event.type.ApplicationType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -27,21 +31,32 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -import static org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_NOTIFY; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_NOTIFY; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER_SERVICE; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE; +import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE_SERVICE; public class RegistryStatCompositeTest { private final String applicationName = "app1"; + private final BaseStatComposite statComposite = new BaseStatComposite() { + @Override + protected void init(ApplicationStatComposite applicationStatComposite, ServiceStatComposite serviceStatComposite, RtStatComposite rtStatComposite) { + applicationStatComposite.init(RegistryMetricsConstants.appKeys); + serviceStatComposite.init(RegistryMetricsConstants.serviceKeys); + rtStatComposite.init(OP_TYPE_REGISTER, OP_TYPE_SUBSCRIBE, OP_TYPE_NOTIFY, OP_TYPE_REGISTER_SERVICE, OP_TYPE_SUBSCRIBE_SERVICE); + } + }; @Test void testInit() { - RegistryStatComposite statComposite = new RegistryStatComposite(); - Assertions.assertEquals(statComposite.applicationNumStats.size(), RegistryEvent.ApplicationType.values().length); - //(rt)5 * (register,subscribe,notify)3 - Assertions.assertEquals(5 * 3, statComposite.appRtStats.size()); - statComposite.applicationNumStats.values().forEach((v -> + Assertions.assertEquals(statComposite.getApplicationStatComposite().getApplicationNumStats().size(), ApplicationType.values().length); + //(rt)5 * (register,subscribe,notify,register.service,subscribe.service)5 + Assertions.assertEquals(5 * 5, statComposite.getRtStatComposite().getRtStats().size()); + statComposite.getApplicationStatComposite().getApplicationNumStats().values().forEach((v -> Assertions.assertEquals(v, new ConcurrentHashMap<>()))); - statComposite.appRtStats.forEach(rtContainer -> + statComposite.getRtStatComposite().getRtStats().forEach(rtContainer -> { for (Map.Entry entry : rtContainer.entrySet()) { Assertions.assertEquals(0L, rtContainer.getValueSupplier().apply(entry.getKey())); @@ -51,17 +66,15 @@ public class RegistryStatCompositeTest { @Test void testIncrement() { - RegistryStatComposite statComposite = new RegistryStatComposite(); - statComposite.increment(RegistryEvent.ApplicationType.R_TOTAL, applicationName); - Assertions.assertEquals(1L, statComposite.applicationNumStats.get(RegistryEvent.ApplicationType.R_TOTAL).get(applicationName).get()); + statComposite.incrementApp(ApplicationType.R_TOTAL.getMetricsKey(), applicationName, 1); + Assertions.assertEquals(1L, statComposite.getApplicationStatComposite().getApplicationNumStats().get(ApplicationType.R_TOTAL.getMetricsKey()).get(applicationName).get()); } @Test void testCalcRt() { - RegistryStatComposite statComposite = new RegistryStatComposite(); - statComposite.calcApplicationRt(applicationName, OP_TYPE_NOTIFY, 10L); - Assertions.assertTrue(statComposite.appRtStats.stream().anyMatch(longContainer -> longContainer.specifyType(OP_TYPE_NOTIFY))); - Optional> subContainer = statComposite.appRtStats.stream().filter(longContainer -> longContainer.specifyType(OP_TYPE_NOTIFY)).findFirst(); + statComposite.calcApplicationRt(applicationName, OP_TYPE_NOTIFY.getType(), 10L); + Assertions.assertTrue(statComposite.getRtStatComposite().getRtStats().stream().anyMatch(longContainer -> longContainer.specifyType(OP_TYPE_NOTIFY.getType()))); + Optional> subContainer = statComposite.getRtStatComposite().getRtStats().stream().filter(longContainer -> longContainer.specifyType(OP_TYPE_NOTIFY.getType())).findFirst(); subContainer.ifPresent(v -> Assertions.assertEquals(10L, v.get(applicationName).longValue())); } } diff --git a/dubbo-monitor/dubbo-monitor-default/src/main/java/org/apache/dubbo/monitor/dubbo/MetricsFilter.java b/dubbo-monitor/dubbo-monitor-default/src/main/java/org/apache/dubbo/monitor/dubbo/MetricsFilter.java index f4d0d54e03..9ee73d03de 100644 --- a/dubbo-monitor/dubbo-monitor-default/src/main/java/org/apache/dubbo/monitor/dubbo/MetricsFilter.java +++ b/dubbo-monitor/dubbo-monitor-default/src/main/java/org/apache/dubbo/monitor/dubbo/MetricsFilter.java @@ -253,7 +253,7 @@ public class MetricsFilter implements Filter, ExtensionAccessorAware, ScopeModel List res = collector.build(); res.addAll(getThreadPoolMessage()); - return AsyncRpcResult.newDefaultAsyncResult(JsonUtils.getJson().toJson(res), invocation); + return AsyncRpcResult.newDefaultAsyncResult(JsonUtils.toJson(res), invocation); } @Override diff --git a/dubbo-native-plugin/pom.xml b/dubbo-native-plugin/pom.xml index c41fa9d15e..f3bf948f03 100644 --- a/dubbo-native-plugin/pom.xml +++ b/dubbo-native-plugin/pom.xml @@ -36,13 +36,13 @@ org.apache.maven maven-plugin-api - 3.9.0 + 3.9.1 org.apache.maven maven-core - 3.9.0 + 3.9.1 provided diff --git a/dubbo-native-plugin/src/main/resources/META-INF/native-image/reflect-config.json b/dubbo-native-plugin/src/main/resources/META-INF/native-image/reflect-config.json index 59853eab5b..36afcc0820 100644 --- a/dubbo-native-plugin/src/main/resources/META-INF/native-image/reflect-config.json +++ b/dubbo-native-plugin/src/main/resources/META-INF/native-image/reflect-config.json @@ -3020,7 +3020,7 @@ "allPublicMethods": true }, { - "name": "org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster", + "name": "org.apache.dubbo.metrics.event.MetricsDispatcher", "allPublicMethods": true, "methods": [ { diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GetAddress.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GetAddress.java new file mode 100644 index 0000000000..8be7fbd9f2 --- /dev/null +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GetAddress.java @@ -0,0 +1,222 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.qos.command.impl; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.utils.JsonUtils; +import org.apache.dubbo.qos.api.BaseCommand; +import org.apache.dubbo.qos.api.Cmd; +import org.apache.dubbo.qos.api.CommandContext; +import org.apache.dubbo.qos.api.PermissionLevel; +import org.apache.dubbo.registry.Registry; +import org.apache.dubbo.registry.client.migration.MigrationInvoker; +import org.apache.dubbo.registry.client.migration.model.MigrationStep; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.cluster.ClusterInvoker; +import org.apache.dubbo.rpc.cluster.Directory; +import org.apache.dubbo.rpc.cluster.directory.AbstractDirectory; +import org.apache.dubbo.rpc.model.ConsumerModel; +import org.apache.dubbo.rpc.model.FrameworkModel; +import org.apache.dubbo.rpc.model.FrameworkServiceRepository; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@Cmd(name = "getAddress", + summary = "Get service available address ", + example = {"getAddress com.example.DemoService", "getAddress group/com.example.DemoService"}, + requiredPermissionLevel = PermissionLevel.PRIVATE) +public class GetAddress implements BaseCommand { + public final FrameworkServiceRepository serviceRepository; + + public GetAddress(FrameworkModel frameworkModel) { + this.serviceRepository = frameworkModel.getServiceRepository(); + } + + @Override + @SuppressWarnings("unchecked") + public String execute(CommandContext commandContext, String[] args) { + if (args == null || args.length != 1) { + return "Invalid parameters, please input like getAddress com.example.DemoService"; + } + + String serviceName = args[0]; + + StringBuilder plainOutput = new StringBuilder(); + Map jsonOutput = new HashMap<>(); + + for (ConsumerModel consumerModel : serviceRepository.allConsumerModels()) { + if (serviceName.equals(consumerModel.getServiceKey())) { + appendConsumer(plainOutput, jsonOutput, consumerModel); + } + } + + if (commandContext.isHttp()) { + return JsonUtils.toJson(jsonOutput); + } else { + return plainOutput.toString(); + } + } + + private static void appendConsumer(StringBuilder plainOutput, Map jsonOutput, ConsumerModel consumerModel) { + plainOutput.append("ConsumerModel: ") + .append(consumerModel.getServiceKey()) + .append("@") + .append(Integer.toHexString(System.identityHashCode(consumerModel))) + .append("\n\n"); + Map consumerMap = new HashMap<>(); + jsonOutput.put(consumerModel.getServiceKey() + "@" + Integer.toHexString(System.identityHashCode(consumerModel)), consumerMap); + + Object object = consumerModel.getServiceMetadata().getAttribute(CommonConstants.CURRENT_CLUSTER_INVOKER_KEY); + Map> invokerMap; + if (object instanceof Map) { + invokerMap = (Map>) object; + for (Map.Entry> entry : invokerMap.entrySet()) { + appendInvokers(plainOutput, consumerMap, entry); + } + } + } + + private static void appendInvokers(StringBuilder plainOutput, Map consumerMap, Map.Entry> entry) { + URL registryUrl = entry.getKey().getUrl(); + + plainOutput.append("Registry: ") + .append(registryUrl) + .append("\n"); + Map registryMap = new HashMap<>(); + consumerMap.put(registryUrl.toString(), registryMap); + + MigrationInvoker migrationInvoker = entry.getValue(); + + MigrationStep migrationStep = migrationInvoker.getMigrationStep(); + plainOutput.append("MigrationStep: ") + .append(migrationStep) + .append("\n\n"); + registryMap.put("MigrationStep", migrationStep); + + Map invokersMap = new HashMap<>(); + registryMap.put("Invokers", invokersMap); + + URL originConsumerUrl = RpcContext.getServiceContext().getConsumerUrl(); + RpcContext.getServiceContext().setConsumerUrl(migrationInvoker.getConsumerUrl()); + + appendInterfaceLevel(plainOutput, migrationInvoker, invokersMap); + appendAppLevel(plainOutput, migrationInvoker, invokersMap); + + RpcContext.getServiceContext().setConsumerUrl(originConsumerUrl); + } + + private static void appendAppLevel(StringBuilder plainOutput, MigrationInvoker migrationInvoker, Map invokersMap) { + Map appMap = new HashMap<>(); + invokersMap.put("Application-Level", appMap); + Optional.ofNullable(migrationInvoker.getServiceDiscoveryInvoker()) + .ifPresent(i -> plainOutput.append("Application-Level: \n")); + Optional.ofNullable(migrationInvoker.getServiceDiscoveryInvoker()) + .map(ClusterInvoker::getDirectory) + .map(Directory::getAllInvokers) + .ifPresent(invokers -> { + List invokerUrls = new LinkedList<>(); + plainOutput.append("All Invokers: \n"); + for (org.apache.dubbo.rpc.Invoker invoker : invokers) { + invokerUrls.add(invoker.getUrl().toFullString()); + plainOutput.append(invoker.getUrl().toFullString()).append("\n"); + } + plainOutput.append("\n"); + appMap.put("All", invokerUrls); + }); + Optional.ofNullable(migrationInvoker.getServiceDiscoveryInvoker()) + .map(ClusterInvoker::getDirectory) + .map(s -> (AbstractDirectory) s) + .map(AbstractDirectory::getValidInvokers) + .ifPresent(invokers -> { + List invokerUrls = new LinkedList<>(); + plainOutput.append("Valid Invokers: \n"); + for (org.apache.dubbo.rpc.Invoker invoker : invokers) { + invokerUrls.add(invoker.getUrl().toFullString()); + plainOutput.append(invoker.getUrl().toFullString()).append("\n"); + } + plainOutput.append("\n"); + appMap.put("Valid", invokerUrls); + }); + Optional.ofNullable(migrationInvoker.getServiceDiscoveryInvoker()) + .map(ClusterInvoker::getDirectory) + .map(s -> (AbstractDirectory) s) + .map(AbstractDirectory::getDisabledInvokers) + .ifPresent(invokers -> { + List invokerUrls = new LinkedList<>(); + plainOutput.append("Disabled Invokers: \n"); + for (org.apache.dubbo.rpc.Invoker invoker : invokers) { + invokerUrls.add(invoker.getUrl().toFullString()); + plainOutput.append(invoker.getUrl().toFullString()).append("\n"); + } + plainOutput.append("\n"); + appMap.put("Disabled", invokerUrls); + }); + } + + private static void appendInterfaceLevel(StringBuilder plainOutput, MigrationInvoker migrationInvoker, Map invokersMap) { + Map interfaceMap = new HashMap<>(); + invokersMap.put("Interface-Level", interfaceMap); + Optional.ofNullable(migrationInvoker.getInvoker()) + .ifPresent(i -> plainOutput.append("Interface-Level: \n")); + Optional.ofNullable(migrationInvoker.getInvoker()) + .map(ClusterInvoker::getDirectory) + .map(Directory::getAllInvokers) + .ifPresent(invokers -> { + List invokerUrls = new LinkedList<>(); + plainOutput.append("All Invokers: \n"); + for (org.apache.dubbo.rpc.Invoker invoker : invokers) { + invokerUrls.add(invoker.getUrl().toFullString()); + plainOutput.append(invoker.getUrl().toFullString()).append("\n"); + } + plainOutput.append("\n"); + interfaceMap.put("All", invokerUrls); + }); + Optional.ofNullable(migrationInvoker.getInvoker()) + .map(ClusterInvoker::getDirectory) + .map(s -> (AbstractDirectory) s) + .map(AbstractDirectory::getValidInvokers) + .ifPresent(invokers -> { + List invokerUrls = new LinkedList<>(); + plainOutput.append("Valid Invokers: \n"); + for (org.apache.dubbo.rpc.Invoker invoker : invokers) { + invokerUrls.add(invoker.getUrl().toFullString()); + plainOutput.append(invoker.getUrl().toFullString()).append("\n"); + } + plainOutput.append("\n"); + interfaceMap.put("Valid", invokerUrls); + }); + Optional.ofNullable(migrationInvoker.getInvoker()) + .map(ClusterInvoker::getDirectory) + .map(s -> (AbstractDirectory) s) + .map(AbstractDirectory::getDisabledInvokers) + .ifPresent(invokers -> { + List invokerUrls = new LinkedList<>(); + plainOutput.append("Disabled Invokers: \n"); + for (org.apache.dubbo.rpc.Invoker invoker : invokers) { + invokerUrls.add(invoker.getUrl().toFullString()); + plainOutput.append(invoker.getUrl().toFullString()).append("\n"); + } + plainOutput.append("\n"); + interfaceMap.put("Disabled", invokerUrls); + }); + } +} diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GetConfig.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GetConfig.java index 11e8884907..74f08ba729 100644 --- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GetConfig.java +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GetConfig.java @@ -30,6 +30,7 @@ import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfigBase; import org.apache.dubbo.config.SslConfig; +import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.context.ModuleConfigManager; import org.apache.dubbo.qos.api.BaseCommand; @@ -65,7 +66,7 @@ public class GetConfig implements BaseCommand { appendFrameworkConfig(args, plainOutput, frameworkMap); if (http) { - return JsonUtils.getJson().toJson(frameworkMap); + return JsonUtils.toJson(frameworkMap); } else { return plainOutput.toString(); } @@ -108,6 +109,9 @@ public class GetConfig implements BaseCommand { Optional metricsConfig = configManager.getMetrics(); metricsConfig.ifPresent(config -> appendConfig("MetricsConfig", config.getId(), config, plainOutput, applicationMap, args)); + Optional tracingConfig = configManager.getTracing(); + tracingConfig.ifPresent(config -> appendConfig("TracingConfig", config.getId(), config, plainOutput, applicationMap, args)); + Optional monitorConfig = configManager.getMonitor(); monitorConfig.ifPresent(config -> appendConfig("MonitorConfig", config.getId(), config, plainOutput, applicationMap, args)); diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/InvokeTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/InvokeTelnet.java index f9bdc1b393..128b174972 100644 --- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/InvokeTelnet.java +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/InvokeTelnet.java @@ -88,7 +88,7 @@ public class InvokeTelnet implements BaseCommand { List list; try { - list = JsonUtils.getJson().toJavaList("[" + param + "]", Object.class); + list = JsonUtils.toJavaList("[" + param + "]", Object.class); } catch (Throwable t) { return "Invalid json argument, cause: " + t.getMessage(); } @@ -156,7 +156,7 @@ public class InvokeTelnet implements BaseCommand { } long end = System.currentTimeMillis(); buf.append("\r\nresult: "); - buf.append(JsonUtils.getJson().toJson(result.recreate())); + buf.append(JsonUtils.toJson(result.recreate())); buf.append("\r\nelapsed: "); buf.append(end - start); buf.append(" ms."); diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SerializeCheckStatus.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SerializeCheckStatus.java index f2cda861cc..9c8ac498a0 100644 --- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SerializeCheckStatus.java +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SerializeCheckStatus.java @@ -45,7 +45,7 @@ public class SerializeCheckStatus implements BaseCommand { result.put("allowedPrefix", serializeCheckUtils.getAllowedList()); result.put("disAllowedPrefix", serializeCheckUtils.getDisAllowedList()); - return JsonUtils.getJson().toJson(result); + return JsonUtils.toJson(result); } else { return "CheckStatus: " + serializeCheckUtils.getStatus() + "\n\n" + "CheckSerializable: " + serializeCheckUtils.isCheckSerializable() + "\n\n" + diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SerializeWarnedClasses.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SerializeWarnedClasses.java index 8a1d053568..a3668d78a4 100644 --- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SerializeWarnedClasses.java +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SerializeWarnedClasses.java @@ -41,7 +41,7 @@ public class SerializeWarnedClasses implements BaseCommand { if (commandContext.isHttp()) { Map result = new HashMap<>(); result.put("warnedClasses", serializeCheckUtils.getWarnedClasses()); - return JsonUtils.getJson().toJson(result); + return JsonUtils.toJson(result); } else { return "WarnedClasses: \n" + serializeCheckUtils.getWarnedClasses().stream().sorted().collect(Collectors.joining("\n")) + diff --git a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.api.BaseCommand b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.api.BaseCommand index c28bf0d97b..78825644ed 100644 --- a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.api.BaseCommand +++ b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.api.BaseCommand @@ -35,3 +35,4 @@ switchLogLevel=org.apache.dubbo.qos.command.impl.SwitchLogLevel serializeCheckStatus=org.apache.dubbo.qos.command.impl.SerializeCheckStatus serializeWarnedClasses=org.apache.dubbo.qos.command.impl.SerializeWarnedClasses getConfig=org.apache.dubbo.qos.command.impl.GetConfig +getAddress=org.apache.dubbo.qos.command.impl.GetAddress diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java index b7f519ee42..a06a76408c 100644 --- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java +++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java @@ -25,6 +25,7 @@ import org.apache.dubbo.qos.command.impl.DisableSimpleProfiler; import org.apache.dubbo.qos.command.impl.EnableDetailProfiler; import org.apache.dubbo.qos.command.impl.EnableRouterSnapshot; import org.apache.dubbo.qos.command.impl.EnableSimpleProfiler; +import org.apache.dubbo.qos.command.impl.GetAddress; import org.apache.dubbo.qos.command.impl.GetConfig; import org.apache.dubbo.qos.command.impl.GetEnabledRouterSnapshot; import org.apache.dubbo.qos.command.impl.GetRecentRouterSnapshot; @@ -121,6 +122,7 @@ class CommandHelperTest { expectedClasses.add(SerializeCheckStatus.class); expectedClasses.add(SerializeWarnedClasses.class); expectedClasses.add(GetConfig.class); + expectedClasses.add(GetAddress.class); assertThat(classes, containsInAnyOrder(expectedClasses.toArray(new Class[0]))); } diff --git a/dubbo-plugin/dubbo-qos/src/test/resources/security/serialize.allowlist b/dubbo-plugin/dubbo-qos/src/test/resources/security/serialize.allowlist new file mode 100644 index 0000000000..74b8dcef99 --- /dev/null +++ b/dubbo-plugin/dubbo-qos/src/test/resources/security/serialize.allowlist @@ -0,0 +1,20 @@ +# +# +# 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. +# +# + +org.apache.dubbo diff --git a/dubbo-plugin/dubbo-security/pom.xml b/dubbo-plugin/dubbo-security/pom.xml index f0a5a7bdaa..64333a8ff5 100644 --- a/dubbo-plugin/dubbo-security/pom.xml +++ b/dubbo-plugin/dubbo-security/pom.xml @@ -26,7 +26,7 @@ false - 3.21.12 + 3.22.2 1.41.0 @@ -103,7 +103,7 @@ kr.motd.maven os-maven-plugin - 1.6.2 + 1.7.1 diff --git a/dubbo-plugin/dubbo-security/src/main/java/org/apache/dubbo/security/cert/CertDeployerListener.java b/dubbo-plugin/dubbo-security/src/main/java/org/apache/dubbo/security/cert/CertDeployerListener.java index 8edd581d11..2ed9be0e76 100644 --- a/dubbo-plugin/dubbo-security/src/main/java/org/apache/dubbo/security/cert/CertDeployerListener.java +++ b/dubbo-plugin/dubbo-security/src/main/java/org/apache/dubbo/security/cert/CertDeployerListener.java @@ -30,6 +30,11 @@ public class CertDeployerListener implements ApplicationDeployListener { dubboCertManager = frameworkModel.getBeanFactory().getBean(DubboCertManager.class); } + @Override + public void onInitialize(ApplicationModel scopeModel) { + + } + @Override public void onStarting(ApplicationModel scopeModel) { scopeModel.getApplicationConfigManager().getSsl().ifPresent(sslConfig -> { diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java index 5e692f4538..9da383d771 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java @@ -26,9 +26,8 @@ import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.metadata.report.MetadataReportInstance; import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; +import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.metadata.event.MetadataEvent; -import org.apache.dubbo.metrics.model.TimePair; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.registry.client.metadata.MetadataUtils; @@ -52,7 +51,6 @@ import static org.apache.dubbo.common.constants.CommonConstants.METADATA_INFO_CA import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_LOCAL_FILE_CACHE_ENABLED; import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_FETCH_INSTANCE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_LOAD_METADATA; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY; import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION; @@ -100,32 +98,32 @@ public abstract class AbstractServiceDiscovery implements ServiceDiscovery { this.metadataInfo = new MetadataInfo(serviceName); boolean localCacheEnabled = registryURL.getParameter(REGISTRY_LOCAL_FILE_CACHE_ENABLED, true); this.metaCacheManager = new MetaCacheManager(localCacheEnabled, getCacheNameSuffix(), - applicationModel.getFrameworkModel().getBeanFactory() - .getBean(FrameworkExecutorRepository.class).getCacheRefreshingScheduledExecutor()); + applicationModel.getFrameworkModel().getBeanFactory() + .getBean(FrameworkExecutorRepository.class).getCacheRefreshingScheduledExecutor()); int metadataInfoCacheExpireTime = registryURL.getParameter(METADATA_INFO_CACHE_EXPIRE_KEY, DEFAULT_METADATA_INFO_CACHE_EXPIRE); int metadataInfoCacheSize = registryURL.getParameter(METADATA_INFO_CACHE_SIZE_KEY, DEFAULT_METADATA_INFO_CACHE_SIZE); this.refreshCacheFuture = applicationModel.getFrameworkModel().getBeanFactory() - .getBean(FrameworkExecutorRepository.class).getSharedScheduledExecutor() - .scheduleAtFixedRate(() -> { - try { - while (metadataInfos.size() > metadataInfoCacheSize) { - AtomicReference oldestRevision = new AtomicReference<>(); - AtomicReference oldestStat = new AtomicReference<>(); - metadataInfos.forEach((k, v) -> { - if (System.currentTimeMillis() - v.getUpdateTime() > metadataInfoCacheExpireTime && - (oldestStat.get() == null || oldestStat.get().getUpdateTime() > v.getUpdateTime())) { - oldestRevision.set(k); - oldestStat.set(v); + .getBean(FrameworkExecutorRepository.class).getSharedScheduledExecutor() + .scheduleAtFixedRate(() -> { + try { + while (metadataInfos.size() > metadataInfoCacheSize) { + AtomicReference oldestRevision = new AtomicReference<>(); + AtomicReference oldestStat = new AtomicReference<>(); + metadataInfos.forEach((k, v) -> { + if (System.currentTimeMillis() - v.getUpdateTime() > metadataInfoCacheExpireTime && + (oldestStat.get() == null || oldestStat.get().getUpdateTime() > v.getUpdateTime())) { + oldestRevision.set(k); + oldestStat.set(v); + } + }); + if (oldestStat.get() != null) { + metadataInfos.remove(oldestRevision.get(), oldestStat.get()); } - }); - if (oldestStat.get() != null) { - metadataInfos.remove(oldestRevision.get(), oldestStat.get()); } + } catch (Throwable t) { + logger.error(INTERNAL_ERROR, "", "", "Error occurred when clean up metadata info cache.", t); } - } catch (Throwable t) { - logger.error(INTERNAL_ERROR, "", "", "Error occurred when clean up metadata info cache.", t); - } - }, metadataInfoCacheExpireTime / 2, metadataInfoCacheExpireTime / 2, TimeUnit.MILLISECONDS); + }, metadataInfoCacheExpireTime / 2, metadataInfoCacheExpireTime / 2, TimeUnit.MILLISECONDS); } @@ -134,12 +132,11 @@ public abstract class AbstractServiceDiscovery implements ServiceDiscovery { if (isDestroy) { return; } - this.serviceInstance = createServiceInstance(this.metadataInfo); - if (!isValidInstance(this.serviceInstance)) { - logger.warn(REGISTRY_FAILED_FETCH_INSTANCE, "", "", "No valid instance found, stop registering instance address to registry."); + ServiceInstance serviceInstance = createServiceInstance(this.metadataInfo); + if (!isValidInstance(serviceInstance)) { return; } - + this.serviceInstance = serviceInstance; boolean revisionUpdated = calOrUpdateInstanceRevision(this.serviceInstance); if (revisionUpdated) { reportMetadata(this.metadataInfo); @@ -159,15 +156,12 @@ public abstract class AbstractServiceDiscovery implements ServiceDiscovery { } if (this.serviceInstance == null) { - this.serviceInstance = createServiceInstance(this.metadataInfo); - } else if (!isValidInstance(this.serviceInstance)) { - ServiceInstanceMetadataUtils.customizeInstance(this.serviceInstance, this.applicationModel); + register(); } if (!isValidInstance(this.serviceInstance)) { return; } - ServiceInstance oldServiceInstance = this.serviceInstance; DefaultServiceInstance newServiceInstance = new DefaultServiceInstance((DefaultServiceInstance) oldServiceInstance); boolean revisionUpdated = calOrUpdateInstanceRevision(newServiceInstance); @@ -229,18 +223,16 @@ public abstract class AbstractServiceDiscovery implements ServiceDiscovery { int triedTimes = 0; while (triedTimes < 3) { - TimePair timePair = TimePair.start(); - GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class); - eventMulticaster.publishEvent(new MetadataEvent.SubscribeEvent(applicationModel, timePair)); + metadata = MetricsEventBus.post(MetadataEvent.toSubscribeEvent(applicationModel), + () -> MetadataUtils.getRemoteMetadata(revision, instances, metadataReport), + result -> result != MetadataInfo.EMPTY + ); - metadata = MetadataUtils.getRemoteMetadata(revision, instances, metadataReport); if (metadata != MetadataInfo.EMPTY) {// succeeded metadata.init(); - eventMulticaster.publishFinishEvent(new MetadataEvent.SubscribeEvent(applicationModel, timePair)); break; } else {// failed - eventMulticaster.publishErrorEvent(new MetadataEvent.SubscribeEvent(applicationModel, timePair)); if (triedTimes > 0) { if (logger.isDebugEnabled()) { logger.debug("Retry the " + triedTimes + " times to get metadata for revision=" + revision); @@ -361,19 +353,15 @@ public abstract class AbstractServiceDiscovery implements ServiceDiscovery { if (metadataInfo == null) { return; } - TimePair timePair = TimePair.start(); - GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class); if (metadataReport != null) { SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(serviceName, metadataInfo.getRevision()); if ((DEFAULT_METADATA_STORAGE_TYPE.equals(metadataType) && metadataReport.shouldReportMetadata()) || REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) { - try { - eventMulticaster.publishEvent(new MetadataEvent.PushEvent(applicationModel, timePair)); - metadataReport.publishAppMetadata(identifier, metadataInfo); - eventMulticaster.publishFinishEvent(new MetadataEvent.PushEvent(applicationModel, timePair)); - } catch (IllegalStateException e) { - eventMulticaster.publishErrorEvent(new MetadataEvent.PushEvent(applicationModel, timePair)); - throw e; - } + MetricsEventBus.post(MetadataEvent.toPushEvent(applicationModel), + () -> + { + metadataReport.publishAppMetadata(identifier, metadataInfo); + return null; + }); } } MetadataInfo clonedMetadataInfo = metadataInfo.clone(); diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java index 38c8efd1eb..266615de54 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java @@ -235,7 +235,7 @@ public class DefaultServiceInstance implements ServiceInstance { public List getEndpoints() { if (endpoints == null) { - endpoints = new LinkedList<>(JsonUtils.getJson().toJavaList(metadata.get(ENDPOINTS), Endpoint.class)); + endpoints = new LinkedList<>(JsonUtils.toJavaList(metadata.get(ENDPOINTS), Endpoint.class)); } return endpoints; } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ReflectionBasedServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ReflectionBasedServiceDiscovery.java index 61537f88c3..d6f37d6f0f 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ReflectionBasedServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ReflectionBasedServiceDiscovery.java @@ -139,7 +139,7 @@ public class ReflectionBasedServiceDiscovery extends AbstractServiceDiscovery { } private void updateInstanceMetadata(ServiceInstance serviceInstance) { - String metadataString = JsonUtils.getJson().toJson(serviceInstance.getMetadata()); + String metadataString = JsonUtils.toJson(serviceInstance.getMetadata()); String metadataRevision = RevisionResolver.calRevision(metadataString); // check if metadata updated @@ -196,7 +196,7 @@ public class ReflectionBasedServiceDiscovery extends AbstractServiceDiscovery { // Metadata will be updated by provider callback String metadataString = metadataMap.get(hostId); - serviceInstance.setMetadata(JsonUtils.getJson().toJavaObject(metadataString, Map.class)); + serviceInstance.setMetadata(JsonUtils.toJavaObject(metadataString, Map.class)); } else { // refer from MetadataUtils, this proxy is different from the one used to refer exportedURL MetadataService metadataService = getMetadataServiceProxy(serviceInstance); @@ -215,12 +215,12 @@ public class ReflectionBasedServiceDiscovery extends AbstractServiceDiscovery { } }); metadataMap.put(hostId, metadata); - serviceInstance.setMetadata(JsonUtils.getJson().toJavaObject(metadata, Map.class)); + serviceInstance.setMetadata(JsonUtils.toJavaObject(metadata, Map.class)); } } public final void notifyListener(String serviceName, ServiceInstancesChangedListener listener, List instances) { - String serviceInstanceRevision = RevisionResolver.calRevision(JsonUtils.getJson().toJson(instances)); + String serviceInstanceRevision = RevisionResolver.calRevision(JsonUtils.toJson(instances)); boolean changed = !serviceInstanceRevision.equalsIgnoreCase( serviceInstanceRevisionMap.put(serviceName, serviceInstanceRevision)); diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java index 0714987b38..003892c6ca 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java @@ -25,8 +25,7 @@ import org.apache.dubbo.metadata.AbstractServiceNameMapping; import org.apache.dubbo.metadata.MappingChangedEvent; import org.apache.dubbo.metadata.MappingListener; import org.apache.dubbo.metadata.ServiceNameMapping; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; -import org.apache.dubbo.metrics.model.TimePair; +import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; @@ -36,7 +35,6 @@ import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -326,25 +324,20 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry { serviceListeners.put(serviceNamesKey, serviceInstancesChangedListener); } - ApplicationModel applicationModel = Optional.ofNullable(url.getApplicationModel()).orElse(ApplicationModel.defaultModel()) ; - GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class); - TimePair timePair = TimePair.start(); - - eventMulticaster.publishEvent(new RegistryEvent.MetricsServiceSubscribeEvent(applicationModel, timePair, serviceKey)); if (!serviceInstancesChangedListener.isDestroyed()) { listener.addServiceListener(serviceInstancesChangedListener); serviceInstancesChangedListener.addListenerAndNotify(url, listener); - try { - serviceDiscovery.addServiceInstancesChangedListener(serviceInstancesChangedListener); - } catch (Throwable t) { - eventMulticaster.publishErrorEvent(new RegistryEvent.MetricsServiceSubscribeEvent(applicationModel, timePair, serviceKey)); - throw t; - } - eventMulticaster.publishFinishEvent(new RegistryEvent.MetricsServiceSubscribeEvent(applicationModel, timePair, serviceKey)); + ServiceInstancesChangedListener finalServiceInstancesChangedListener = serviceInstancesChangedListener; + + MetricsEventBus.post(RegistryEvent.toSsEvent(url.getApplicationModel(), serviceKey), + () -> { + serviceDiscovery.addServiceInstancesChangedListener(finalServiceInstancesChangedListener); + return null; + } + ); } else { logger.info(String.format("Listener of %s has been destroyed by another thread.", serviceNamesKey)); serviceListeners.remove(serviceNamesKey); - eventMulticaster.publishErrorEvent(new RegistryEvent.MetricsSubscribeEvent(applicationModel, timePair)); } } finally { appSubscriptionLock.unlock(); diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java index 0859c1e9e2..e069ead88c 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java @@ -19,7 +19,6 @@ package org.apache.dubbo.registry.client.event.listener; import org.apache.dubbo.common.ProtocolServiceKey; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; -import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; @@ -28,8 +27,7 @@ import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo; -import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster; -import org.apache.dubbo.metrics.model.TimePair; +import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.client.DefaultServiceInstance; @@ -67,6 +65,8 @@ import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_EMPTY_P import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision; +; + /** * TODO, refactor to move revision-metadata mapping to ServiceDiscovery. Instances should have already been mapped with metadata when reached here. *

@@ -405,25 +405,24 @@ public class ServiceInstancesChangedListener { */ protected void notifyAddressChanged() { - ScopeBeanFactory beanFactory = applicationModel.getFrameworkModel().getBeanFactory(); - SimpleMetricsEventMulticaster eventMulticaster = beanFactory.getOrRegisterBean(SimpleMetricsEventMulticaster.class); + MetricsEventBus.post(RegistryEvent.toNotifyEvent(applicationModel), + () -> { + Map lastNumMap = new HashMap<>(); + // 1 different services + listeners.forEach((serviceKey, listenerSet) -> { + // 2 multiple subscription listener of the same service + for (NotifyListenerWithKey listenerWithKey : listenerSet) { + NotifyListener notifyListener = listenerWithKey.getNotifyListener(); - TimePair timePair = TimePair.start(); - eventMulticaster.publishEvent(new RegistryEvent.MetricsNotifyEvent(applicationModel, timePair, null)); - Map lastNumMap = new HashMap<>(); - // 1 different services - listeners.forEach((serviceKey, listenerSet) -> { - // 2 multiple subscription listener of the same service - for (NotifyListenerWithKey listenerWithKey : listenerSet) { - NotifyListener notifyListener = listenerWithKey.getNotifyListener(); - - List urls = toUrlsWithEmpty(getAddresses(listenerWithKey.getProtocolServiceKey(), notifyListener.getConsumerUrl())); - logger.info("Notify service " + listenerWithKey.getProtocolServiceKey() + " with urls " + urls.size()); - notifyListener.notify(urls); - lastNumMap.put(serviceKey, urls.size()); + List urls = toUrlsWithEmpty(getAddresses(listenerWithKey.getProtocolServiceKey(), notifyListener.getConsumerUrl())); + logger.info("Notify service " + listenerWithKey.getProtocolServiceKey() + " with urls " + urls.size()); + notifyListener.notify(urls); + lastNumMap.put(serviceKey, urls.size()); + } + }); + return lastNumMap; } - }); - eventMulticaster.publishFinishEvent(new RegistryEvent.MetricsNotifyEvent(applicationModel, timePair, lastNumMap)); + ); } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java index 080344574f..13434ceabc 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java @@ -102,7 +102,7 @@ public class ServiceInstanceMetadataUtils { return null; } - return JsonUtils.getJson().toJson(params); + return JsonUtils.toJson(params); } private static Map getParams(URL providerURL) { @@ -174,7 +174,7 @@ public class ServiceInstanceMetadataUtils { endpoints.add(endpoint); }); - metadata.put(ENDPOINTS, JsonUtils.getJson().toJson(endpoints)); + metadata.put(ENDPOINTS, JsonUtils.toJson(endpoints)); } /** diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SpringCloudMetadataServiceURLBuilder.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SpringCloudMetadataServiceURLBuilder.java index 1ffa4445f1..2c15033653 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SpringCloudMetadataServiceURLBuilder.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SpringCloudMetadataServiceURLBuilder.java @@ -44,7 +44,7 @@ public class SpringCloudMetadataServiceURLBuilder implements MetadataServiceURLB if (StringUtils.isBlank(dubboUrlsForJson)) { return Collections.emptyList(); } - List urlStrings = JsonUtils.getJson().toJavaList(dubboUrlsForJson, String.class); + List urlStrings = JsonUtils.toJavaList(dubboUrlsForJson, String.class); return urlStrings.stream().map(URL::valueOf).collect(Collectors.toList()); } } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SpringCloudServiceInstanceNotificationCustomizer.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SpringCloudServiceInstanceNotificationCustomizer.java index 444f08fb69..750eb6e6e9 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SpringCloudServiceInstanceNotificationCustomizer.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SpringCloudServiceInstanceNotificationCustomizer.java @@ -28,24 +28,30 @@ import java.util.concurrent.ConcurrentHashMap; public class SpringCloudServiceInstanceNotificationCustomizer implements ServiceInstanceNotificationCustomizer { @Override public void customize(List serviceInstance) { + if (serviceInstance.isEmpty()) { + return; + } + + if (!serviceInstance.stream().allMatch(instance -> "SPRING_CLOUD".equals(instance.getMetadata("preserved.register.source")))) { + return; + } + for (ServiceInstance instance : serviceInstance) { - if ("SPRING_CLOUD".equals(instance.getMetadata("preserved.register.source"))) { - MetadataInfo.ServiceInfo serviceInfo = new MetadataInfo.ServiceInfo("*", "*", "*", "rest", instance.getPort(), "*", new HashMap<>()); - String revision = "SPRING_CLOUD-" + instance.getServiceName() + "-" + instance.getAddress() + "-" + instance.getPort(); - MetadataInfo metadataInfo = new MetadataInfo(instance.getServiceName(), revision, new ConcurrentHashMap<>(Collections.singletonMap("*", serviceInfo))) { - @Override - public List getMatchedServiceInfos(ProtocolServiceKey consumerProtocolServiceKey) { - getServices().putIfAbsent(consumerProtocolServiceKey.getServiceKeyString(), - new MetadataInfo.ServiceInfo(consumerProtocolServiceKey.getInterfaceName(), - consumerProtocolServiceKey.getGroup(), consumerProtocolServiceKey.getVersion(), - consumerProtocolServiceKey.getProtocol(), instance.getPort(), consumerProtocolServiceKey.getInterfaceName(), new HashMap<>())); - return super.getMatchedServiceInfos(consumerProtocolServiceKey); - } - }; + MetadataInfo.ServiceInfo serviceInfo = new MetadataInfo.ServiceInfo("*", "*", "*", "rest", instance.getPort(), "*", new HashMap<>()); + String revision = "SPRING_CLOUD-" + instance.getServiceName() + "-" + instance.getAddress() + "-" + instance.getPort(); + MetadataInfo metadataInfo = new MetadataInfo(instance.getServiceName(), revision, new ConcurrentHashMap<>(Collections.singletonMap("*", serviceInfo))) { + @Override + public List getMatchedServiceInfos(ProtocolServiceKey consumerProtocolServiceKey) { + getServices().putIfAbsent(consumerProtocolServiceKey.getServiceKeyString(), + new MetadataInfo.ServiceInfo(consumerProtocolServiceKey.getInterfaceName(), + consumerProtocolServiceKey.getGroup(), consumerProtocolServiceKey.getVersion(), + consumerProtocolServiceKey.getProtocol(), instance.getPort(), consumerProtocolServiceKey.getInterfaceName(), new HashMap<>())); + return super.getMatchedServiceInfos(consumerProtocolServiceKey); + } + }; - instance.setServiceMetadata(metadataInfo); - } + instance.setServiceMetadata(metadataInfo); } } } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java index b36139a861..20f55a6edb 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java @@ -179,6 +179,6 @@ public class StandardMetadataServiceURLBuilder implements MetadataServiceURLBuil private Map getMetadataServiceURLsParams(ServiceInstance serviceInstance) { Map metadata = serviceInstance.getMetadata(); String param = metadata.get(METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME); - return isBlank(param) ? emptyMap() : (Map) JsonUtils.getJson().toJavaObject(param, Map.class); + return isBlank(param) ? emptyMap() : (Map) JsonUtils.toJavaObject(param, Map.class); } } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManager.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManager.java index 3a4991f7fe..8cb2c00711 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManager.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManager.java @@ -63,7 +63,7 @@ public class MetaCacheManager extends AbstractCacheManager { @Override protected MetadataInfo toValueType(String value) { - return JsonUtils.getJson().toJavaObject(value, MetadataInfo.class); + return JsonUtils.toJavaObject(value, MetadataInfo.class); } @Override diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationRule.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationRule.java index f08d2d5faf..c42354125a 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationRule.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationRule.java @@ -22,6 +22,7 @@ import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.ServiceNameMapping; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -316,13 +317,13 @@ public class MigrationRule { } public static MigrationRule parse(String rawRule) { - Yaml yaml = new Yaml(new SafeConstructor()); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Map map = yaml.load(rawRule); return parseFromMap(map); } public static String toYaml(MigrationRule rule) { - Constructor constructor = new Constructor(MigrationRule.class); + Constructor constructor = new Constructor(MigrationRule.class, new LoaderOptions()); Yaml yaml = new Yaml(constructor); return yaml.dump(rule); } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java index c171b9b5b0..7ee4da0ed8 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java @@ -287,8 +287,14 @@ public abstract class DynamicDirectory extends AbstractDirectory implement if (isDestroyed() || this.forbidden) { return false; } - return CollectionUtils.isNotEmpty(getValidInvokers()) - && getValidInvokers().stream().anyMatch(Invoker::isAvailable); + for (Invoker validInvoker : getValidInvokers()) { + if (validInvoker.isAvailable()) { + return true; + } else { + addInvalidateInvoker(validInvoker); + } + } + return false; } @Override diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java index fe85a484ab..0fbdcc036e 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java @@ -30,8 +30,7 @@ import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.UrlUtils; -import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; -import org.apache.dubbo.metrics.model.TimePair; +import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.registry.AddressListener; import org.apache.dubbo.remoting.Constants; @@ -120,12 +119,12 @@ public class RegistryDirectory extends DynamicDirectory { @Override public void subscribe(URL url) { ApplicationModel applicationModel = url.getApplicationModel(); - GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class); - TimePair timePair = TimePair.start(); - - eventMulticaster.publishEvent(new RegistryEvent.MetricsSubscribeEvent(applicationModel, timePair)); - super.subscribe(url); - eventMulticaster.publishFinishEvent(new RegistryEvent.MetricsSubscribeEvent(applicationModel, timePair)); + MetricsEventBus.post(RegistryEvent.toSubscribeEvent(applicationModel),() -> + { + super.subscribe(url); + return null; + } + ); if (moduleModel.getModelEnvironment().getConfiguration().convert(Boolean.class, org.apache.dubbo.registry.Constants.ENABLE_CONFIGURATION_LISTEN, true)) { consumerConfigurationListener.addNotifyListener(this); referenceConfigurationListener = new ReferenceConfigurationListener(moduleModel, this, url); diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerTest.java index cc2a11a17f..02b01cf5af 100644 --- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerTest.java +++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerTest.java @@ -167,11 +167,11 @@ class ServiceInstancesChangedListenerTest { app1InstancesWithNoRevision = buildInstances(urlsWithoutRevision); app1InstancesMultipleProtocols = buildInstances(urlsMultipleProtocols); - metadataInfo_111 = JsonUtils.getJson().toJavaObject(metadata_111, MetadataInfo.class); - metadataInfo_222 = JsonUtils.getJson().toJavaObject(metadata_222, MetadataInfo.class); - metadataInfo_333 = JsonUtils.getJson().toJavaObject(metadata_333, MetadataInfo.class); - metadataInfo_444 = JsonUtils.getJson().toJavaObject(metadata_444, MetadataInfo.class); - metadataInfo_555_tri = JsonUtils.getJson().toJavaObject(metadata_555_triple, MetadataInfo.class); + metadataInfo_111 = JsonUtils.toJavaObject(metadata_111, MetadataInfo.class); + metadataInfo_222 = JsonUtils.toJavaObject(metadata_222, MetadataInfo.class); + metadataInfo_333 = JsonUtils.toJavaObject(metadata_333, MetadataInfo.class); + metadataInfo_444 = JsonUtils.toJavaObject(metadata_444, MetadataInfo.class); + metadataInfo_555_tri = JsonUtils.toJavaObject(metadata_555_triple, MetadataInfo.class); serviceDiscovery = Mockito.mock(ServiceDiscovery.class); when(serviceDiscovery.getUrl()).thenReturn(registryURL); diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerWithoutEmptyProtectTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerWithoutEmptyProtectTest.java index 22f77893f2..37e863121c 100644 --- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerWithoutEmptyProtectTest.java +++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerWithoutEmptyProtectTest.java @@ -167,11 +167,11 @@ public class ServiceInstancesChangedListenerWithoutEmptyProtectTest { app1InstancesWithNoRevision = buildInstances(urlsWithoutRevision); app1InstancesMultipleProtocols = buildInstances(urlsMultipleProtocols); - metadataInfo_111 = JsonUtils.getJson().toJavaObject(metadata_111, MetadataInfo.class); - metadataInfo_222 = JsonUtils.getJson().toJavaObject(metadata_222, MetadataInfo.class); - metadataInfo_333 = JsonUtils.getJson().toJavaObject(metadata_333, MetadataInfo.class); - metadataInfo_444 = JsonUtils.getJson().toJavaObject(metadata_444, MetadataInfo.class); - metadataInfo_555_tri = JsonUtils.getJson().toJavaObject(metadata_555_triple, MetadataInfo.class); + metadataInfo_111 = JsonUtils.toJavaObject(metadata_111, MetadataInfo.class); + metadataInfo_222 = JsonUtils.toJavaObject(metadata_222, MetadataInfo.class); + metadataInfo_333 = JsonUtils.toJavaObject(metadata_333, MetadataInfo.class); + metadataInfo_444 = JsonUtils.toJavaObject(metadata_444, MetadataInfo.class); + metadataInfo_555_tri = JsonUtils.toJavaObject(metadata_555_triple, MetadataInfo.class); serviceDiscovery = Mockito.mock(ServiceDiscovery.class); when(serviceDiscovery.getUrl()).thenReturn(registryURL); diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/ProtocolPortsMetadataCustomizerTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/ProtocolPortsMetadataCustomizerTest.java index d5bf6ba89e..3c6919d644 100644 --- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/ProtocolPortsMetadataCustomizerTest.java +++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/ProtocolPortsMetadataCustomizerTest.java @@ -85,7 +85,7 @@ class ProtocolPortsMetadataCustomizerTest { customizer.customize(instance, ApplicationModel.defaultModel()); String endpoints = instance.getMetadata().get(ENDPOINTS); assertNotNull(endpoints); - List endpointList = JsonUtils.getJson().toJavaList(endpoints, DefaultServiceInstance.Endpoint.class); + List endpointList = JsonUtils.toJavaList(endpoints, DefaultServiceInstance.Endpoint.class); assertEquals(2, endpointList.size()); MatcherAssert.assertThat(endpointList, hasItem(hasProperty("protocol", equalTo("dubbo")))); MatcherAssert.assertThat(endpointList, hasItem(hasProperty("port", equalTo(20880)))); diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManagerTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManagerTest.java index f8dc8ca82a..b4e38d5b9a 100644 --- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManagerTest.java +++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManagerTest.java @@ -68,7 +68,7 @@ class MetaCacheManagerTest { assertNull(metadataInfo); Map newMetadatas = new HashMap<>(); - MetadataInfo metadataInfo2 = JsonUtils.getJson().toJavaObject("{\"app\":\"demo2\",\"services\":{\"greeting/org.apache.dubbo.registry.service.DemoService2:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService2\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService2\",\"params\":{\"application\":\"demo-provider2\",\"sayHello.timeout\":\"7000\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}},\"greeting/org.apache.dubbo.registry.service.DemoService:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService\",\"params\":{\"application\":\"demo-provider2\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}}}}\n", MetadataInfo.class); + MetadataInfo metadataInfo2 = JsonUtils.toJavaObject("{\"app\":\"demo2\",\"services\":{\"greeting/org.apache.dubbo.registry.service.DemoService2:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService2\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService2\",\"params\":{\"application\":\"demo-provider2\",\"sayHello.timeout\":\"7000\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}},\"greeting/org.apache.dubbo.registry.service.DemoService:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService\",\"params\":{\"application\":\"demo-provider2\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}}}}\n", MetadataInfo.class); newMetadatas.put("2", metadataInfo2); cacheManager.update(newMetadatas); @@ -87,7 +87,7 @@ class MetaCacheManagerTest { @Test void testCacheDump() { System.setProperty("dubbo.meta.cache.fileName", "not-exist.dubbo.cache"); - MetadataInfo metadataInfo3 = JsonUtils.getJson().toJavaObject("{\"app\":\"demo3\",\"services\":{\"greeting/org.apache.dubbo.registry.service.DemoService2:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService2\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService2\",\"params\":{\"application\":\"demo-provider2\",\"sayHello.timeout\":\"7000\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}},\"greeting/org.apache.dubbo.registry.service.DemoService:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService\",\"params\":{\"application\":\"demo-provider2\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}}}}\n", MetadataInfo.class); + MetadataInfo metadataInfo3 = JsonUtils.toJavaObject("{\"app\":\"demo3\",\"services\":{\"greeting/org.apache.dubbo.registry.service.DemoService2:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService2\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService2\",\"params\":{\"application\":\"demo-provider2\",\"sayHello.timeout\":\"7000\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}},\"greeting/org.apache.dubbo.registry.service.DemoService:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService\",\"params\":{\"application\":\"demo-provider2\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}}}}\n", MetadataInfo.class); MetaCacheManager cacheManager = new MetaCacheManager(); try { cacheManager.put("3", metadataInfo3); diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosAggregateListener.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosAggregateListener.java index 4eafb9b6f5..b8dcaad92f 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosAggregateListener.java +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosAggregateListener.java @@ -16,6 +16,8 @@ */ package org.apache.dubbo.registry.nacos; +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.registry.NotifyListener; @@ -26,12 +28,19 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.regex.Pattern; import java.util.stream.Collectors; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_NACOS_SUB_LEGACY; + public class NacosAggregateListener { + private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NacosAggregateListener.class); private final NotifyListener notifyListener; private final Set serviceNames = new ConcurrentHashSet<>(); private final Map> serviceInstances = new ConcurrentHashMap<>(); + private final AtomicBoolean warned = new AtomicBoolean(false); + private static final Pattern SPLITTED_PATTERN = Pattern.compile(".*:.*:.*:.*"); public NacosAggregateListener(NotifyListener notifyListener) { this.notifyListener = notifyListener; @@ -44,9 +53,21 @@ public class NacosAggregateListener { } else { serviceInstances.put(serviceName, instances); } + if (isLegacyName(serviceName) && instances != null && + !instances.isEmpty() && warned.compareAndSet(false, true)) { + logger.error(REGISTRY_NACOS_SUB_LEGACY, "", "", + "Received not empty notification for legacy service name: " + serviceName + ", " + + "instances: [" + instances.stream().map(Instance::getIp).collect(Collectors.joining(" ,")) + "]. " + + "Please upgrade these Dubbo client(lower than 2.7.3) to the latest version. " + + "Dubbo will remove the support for legacy service name in the future."); + } return serviceInstances.values().stream().flatMap(List::stream).collect(Collectors.toList()); } + private static boolean isLegacyName(String serviceName) { + return !SPLITTED_PATTERN.matcher(serviceName).matches(); + } + public NotifyListener getNotifyListener() { return notifyListener; } diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java index 269aac4b44..baaa2bf437 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java @@ -136,10 +136,12 @@ public class NacosRegistry extends FailbackRegistry { private final Map> originToAggregateListener = new ConcurrentHashMap<>(); private final Map>> nacosListeners = new ConcurrentHashMap<>(); + private final boolean supportLegacyServiceName; public NacosRegistry(URL url, NacosNamingServiceWrapper namingService) { super(url); this.namingService = namingService; + this.supportLegacyServiceName = url.getParameter("nacos.subscribe.legacy-name", true); } @Override @@ -326,11 +328,13 @@ public class NacosRegistry extends FailbackRegistry { if (serviceName.isConcrete()) { // is the concrete service name serviceNames = new LinkedHashSet<>(); serviceNames.add(serviceName.toString()); - // Add the legacy service name since 2.7.6 - String legacySubscribedServiceName = getLegacySubscribedServiceName(url); - if (!serviceName.toString().equals(legacySubscribedServiceName)) { - //avoid duplicated service names - serviceNames.add(legacySubscribedServiceName); + if (supportLegacyServiceName) { + // Add the legacy service name since 2.7.6 + String legacySubscribedServiceName = getLegacySubscribedServiceName(url); + if (!serviceName.toString().equals(legacySubscribedServiceName)) { + //avoid duplicated service names + serviceNames.add(legacySubscribedServiceName); + } } } else { serviceNames = filterServiceNames(serviceName); diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java index f764b74909..3fbe2853c4 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java @@ -49,6 +49,7 @@ import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION; import static org.apache.dubbo.common.function.ThrowableConsumer.execute; import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION; +import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision; import static org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils.createNamingService; import static org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils.getGroup; @@ -78,7 +79,7 @@ public class NacosServiceDiscovery extends AbstractServiceDiscovery { this.namingService = createNamingService(registryURL); // backward compatibility for 3.0.x this.group = Boolean.parseBoolean(ConfigurationUtils.getProperty(applicationModel, NACOS_SD_USE_DEFAULT_GROUP_KEY, "false")) ? - DEFAULT_GROUP : getGroup(registryURL); + DEFAULT_GROUP : getGroup(registryURL); } @Override @@ -105,13 +106,14 @@ public class NacosServiceDiscovery extends AbstractServiceDiscovery { @Override protected void doUpdate(ServiceInstance oldServiceInstance, ServiceInstance newServiceInstance) throws RuntimeException { - if (EMPTY_REVISION.equals(getExportedServicesRevision(newServiceInstance))) { + if (EMPTY_REVISION.equals(getExportedServicesRevision(newServiceInstance)) + || EMPTY_REVISION.equals(oldServiceInstance.getMetadata().get(EXPORTED_SERVICES_REVISION_PROPERTY_NAME))) { super.doUpdate(oldServiceInstance, newServiceInstance); return; } if (!Objects.equals(newServiceInstance.getHost(), oldServiceInstance.getHost()) || - !Objects.equals(newServiceInstance.getPort(), oldServiceInstance.getPort())) { + !Objects.equals(newServiceInstance.getPort(), oldServiceInstance.getPort())) { // Ignore if id changed. Should unregister first. super.doUpdate(oldServiceInstance, newServiceInstance); return; @@ -143,15 +145,15 @@ public class NacosServiceDiscovery extends AbstractServiceDiscovery { @Override public List getInstances(String serviceName) throws NullPointerException { return ThrowableFunction.execute(namingService, service -> - service.selectInstances(serviceName, group, true) - .stream().map((i) -> NacosNamingServiceUtils.toServiceInstance(registryURL, i)) - .collect(Collectors.toList()) + service.selectInstances(serviceName, group, true) + .stream().map((i) -> NacosNamingServiceUtils.toServiceInstance(registryURL, i)) + .collect(Collectors.toList()) ); } @Override public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) - throws NullPointerException, IllegalArgumentException { + throws NullPointerException, IllegalArgumentException { // check if listener has already been added through another interface/service if (!instanceListeners.add(listener)) { return; @@ -228,9 +230,9 @@ public class NacosServiceDiscovery extends AbstractServiceDiscovery { private void handleEvent(NamingEvent event, ServiceInstancesChangedListener listener) { String serviceName = event.getServiceName(); List serviceInstances = event.getInstances() - .stream() - .map((i) -> NacosNamingServiceUtils.toServiceInstance(registryURL, i)) - .collect(Collectors.toList()); + .stream() + .map((i) -> NacosNamingServiceUtils.toServiceInstance(registryURL, i)) + .collect(Collectors.toList()); listener.onEvent(new ServiceInstancesChangedEvent(serviceName, serviceInstances)); } } diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java index a27675b5c4..1dd2ea8896 100644 --- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java @@ -28,6 +28,7 @@ import java.util.concurrent.CountDownLatch; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.x.discovery.ServiceCache; + import org.apache.dubbo.common.URL; import org.apache.dubbo.common.function.ThrowableConsumer; import org.apache.dubbo.common.function.ThrowableFunction; @@ -44,6 +45,7 @@ import org.apache.dubbo.rpc.model.ApplicationModel; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ZOOKEEPER_EXCEPTION; import static org.apache.dubbo.common.function.ThrowableFunction.execute; import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION; +import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.build; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.buildCuratorFramework; @@ -111,7 +113,8 @@ public class ZookeeperServiceDiscovery extends AbstractServiceDiscovery { @Override protected void doUpdate(ServiceInstance oldServiceInstance, ServiceInstance newServiceInstance) throws RuntimeException { - if (EMPTY_REVISION.equals(getExportedServicesRevision(newServiceInstance))) { + if (EMPTY_REVISION.equals(getExportedServicesRevision(newServiceInstance)) + || EMPTY_REVISION.equals(oldServiceInstance.getMetadata().get(EXPORTED_SERVICES_REVISION_PROPERTY_NAME))) { super.doUpdate(oldServiceInstance, newServiceInstance); return; } @@ -119,7 +122,7 @@ public class ZookeeperServiceDiscovery extends AbstractServiceDiscovery { org.apache.curator.x.discovery.ServiceInstance oldInstance = build(oldServiceInstance); org.apache.curator.x.discovery.ServiceInstance newInstance = build(newServiceInstance); if (!Objects.equals(newInstance.getName(), oldInstance.getName()) || - !Objects.equals(newInstance.getId(), oldInstance.getId())) { + !Objects.equals(newInstance.getId(), oldInstance.getId())) { // Ignore if id changed. Should unregister first. super.doUpdate(oldServiceInstance, newServiceInstance); return; @@ -146,7 +149,7 @@ public class ZookeeperServiceDiscovery extends AbstractServiceDiscovery { @Override public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) - throws NullPointerException, IllegalArgumentException { + throws NullPointerException, IllegalArgumentException { // check if listener has already been added through another interface/service if (!instanceListeners.add(listener)) { return; @@ -169,7 +172,7 @@ public class ZookeeperServiceDiscovery extends AbstractServiceDiscovery { watcher.getCacheInstance().close(); } catch (IOException e) { logger.error(REGISTRY_ZOOKEEPER_EXCEPTION, "curator stop watch failed", "", - "Curator Stop service discovery watch failed. Service Name: " + serviceName); + "Curator Stop service discovery watch failed. Service Name: " + serviceName); } } } @@ -190,8 +193,8 @@ public class ZookeeperServiceDiscovery extends AbstractServiceDiscovery { ZookeeperServiceDiscoveryChangeWatcher watcher = watcherCaches.computeIfAbsent(serviceName, name -> { ServiceCache serviceCache = serviceDiscovery.serviceCacheBuilder() - .name(name) - .build(); + .name(name) + .build(); ZookeeperServiceDiscoveryChangeWatcher newer = new ZookeeperServiceDiscoveryChangeWatcher(this, serviceCache, name, latch); serviceCache.addListener(newer); diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Transporters.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Transporters.java index 97062e8d5a..d8dbb4ee45 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Transporters.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Transporters.java @@ -17,7 +17,6 @@ package org.apache.dubbo.remoting; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.Version; import org.apache.dubbo.remoting.transport.ChannelHandlerAdapter; import org.apache.dubbo.remoting.transport.ChannelHandlerDispatcher; @@ -26,12 +25,6 @@ import org.apache.dubbo.remoting.transport.ChannelHandlerDispatcher; */ public class Transporters { - static { - // check duplicate jar package - Version.checkDuplicate(Transporters.class); - Version.checkDuplicate(RemotingException.class); - } - private Transporters() { } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Exchangers.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Exchangers.java index 950b06ac48..c8f9b9da14 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Exchangers.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Exchangers.java @@ -17,7 +17,6 @@ package org.apache.dubbo.remoting.exchange; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.Version; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; @@ -29,12 +28,6 @@ import org.apache.dubbo.remoting.transport.ChannelHandlerAdapter; * Exchanger facade. (API, Static, ThreadSafe) */ public class Exchangers { - - static { - // check duplicate jar package - Version.checkDuplicate(Exchangers.class); - } - private Exchangers() { } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java index 7a92b0202a..1aa2d8ad7e 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java @@ -43,7 +43,10 @@ import org.apache.dubbo.remoting.transport.ExceedPayloadLimitException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_TIMEOUT_SERVER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_EXCEED_PAYLOAD_LIMIT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RESPONSE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_SKIP_UNUSED_STREAM; @@ -172,7 +175,7 @@ public class ExchangeCodec extends TelnetCodec { data = decodeEventData(channel, CodecSupport.deserialize(channel.getUrl(), new ByteArrayInputStream(eventPayload), proto), eventPayload); } } else { - data = decodeResponseData(channel, CodecSupport.deserialize(channel.getUrl(), is, proto), getRequestData(id)); + data = decodeResponseData(channel, CodecSupport.deserialize(channel.getUrl(), is, proto), getRequestData(channel, res, id)); } res.setResult(data); } else { @@ -217,16 +220,21 @@ public class ExchangeCodec extends TelnetCodec { } } - protected Object getRequestData(long id) { + protected Object getRequestData(Channel channel, Response response, long id) { DefaultFuture future = DefaultFuture.getFuture(id); - if (future == null) { - return null; + if (future != null) { + Request req = future.getRequest(); + if (req != null) { + return req.getData(); + } } - Request req = future.getRequest(); - if (req == null) { - return null; - } - return req.getData(); + + logger.warn(PROTOCOL_TIMEOUT_SERVER, "", "", "The timeout response finally returned at " + + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) + + ", response status is " + response.getStatus() + ", response id is " + response.getId() + + (channel == null ? "" : ", channel: " + channel.getLocalAddress() + + " -> " + channel.getRemoteAddress()) + ", please check provider side for detailed result."); + throw new IllegalArgumentException("Failed to find any request match the response, response id: " + id); } protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException { @@ -410,7 +418,7 @@ public class ExchangeCodec extends TelnetCodec { } private void encodeEventData(ObjectOutput out, Object data) throws IOException { - out.writeEvent(data); + out.writeEvent((String) data); } @Deprecated @@ -435,7 +443,7 @@ public class ExchangeCodec extends TelnetCodec { try { if (eventBytes != null) { int dataLen = eventBytes.length; - int threshold = ConfigurationUtils.getSystemConfiguration(channel.getUrl().getScopeModel()).getInt("deserialization.event.size", 50); + int threshold = ConfigurationUtils.getSystemConfiguration(channel.getUrl().getScopeModel()).getInt("deserialization.event.size", 15); if (dataLen > threshold) { throw new IllegalArgumentException("Event data too long, actual size " + threshold + ", threshold " + threshold + " rejected for security consideration."); } @@ -489,12 +497,12 @@ public class ExchangeCodec extends TelnetCodec { } private Object finishRespWhenOverPayload(Channel channel, long size, byte[] header) { - int payload = getPayload(channel); - boolean overPayload = isOverPayload(payload, size); - if (overPayload) { - long reqId = Bytes.bytes2long(header, 4); - byte flag = header[2]; - if ((flag & FLAG_REQUEST) == 0) { + byte flag = header[2]; + if ((flag & FLAG_REQUEST) == 0) { + int payload = getPayload(channel); + boolean overPayload = isOverPayload(payload, size); + if (overPayload) { + long reqId = Bytes.bytes2long(header, 4); Response res = new Response(reqId); if ((flag & FLAG_EVENT) != 0) { res.setEvent(true); diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java index 9ca69e9df1..c35739a88e 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java @@ -19,7 +19,6 @@ package org.apache.dubbo.remoting.exchange.support; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.resource.GlobalResourceInitializer; -import org.apache.dubbo.common.threadpool.ThreadlessExecutor; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.common.timer.Timeout; import org.apache.dubbo.common.timer.Timer; @@ -124,10 +123,6 @@ public class DefaultFuture extends CompletableFuture { public static DefaultFuture newFuture(Channel channel, Request request, int timeout, ExecutorService executor) { final DefaultFuture future = new DefaultFuture(channel, request, timeout); future.setExecutor(executor); - // ThreadlessExecutor needs to hold the waiting future in case of circuit return. - if (executor instanceof ThreadlessExecutor) { - ((ThreadlessExecutor) executor).setWaitingFuture(future); - } // timeout check timeoutCheck(future); return future; @@ -223,16 +218,6 @@ public class DefaultFuture extends CompletableFuture { } else { this.completeExceptionally(new RemotingException(channel, res.getErrorMessage())); } - - // the result is returning, but the caller thread may still wait - // to avoid endless waiting for whatever reason, notify caller thread to return. - if (executor != null && executor instanceof ThreadlessExecutor) { - ThreadlessExecutor threadlessExecutor = (ThreadlessExecutor) executor; - if (threadlessExecutor.isWaiting()) { - threadlessExecutor.notifyReturn(new IllegalStateException("The result has returned, but the biz thread is still waiting" + - " which is not an expected state, interrupt the thread manually by returning an exception.")); - } - } } private long getId() { @@ -288,8 +273,9 @@ public class DefaultFuture extends CompletableFuture { return; } - if (future.getExecutor() != null) { - future.getExecutor().execute(() -> notifyTimeout(future)); + ExecutorService executor = future.getExecutor(); + if (executor != null && !executor.isShutdown()) { + executor.execute(() -> notifyTimeout(future)); } else { notifyTimeout(future); } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/AbstractTimerTask.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/AbstractTimerTask.java index 3779d9917f..ce5c5e6014 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/AbstractTimerTask.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/AbstractTimerTask.java @@ -17,8 +17,8 @@ package org.apache.dubbo.remoting.exchange.support.header; +import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.common.timer.Timeout; -import org.apache.dubbo.common.timer.Timer; import org.apache.dubbo.common.timer.TimerTask; import org.apache.dubbo.remoting.Channel; @@ -31,17 +31,22 @@ import java.util.concurrent.TimeUnit; public abstract class AbstractTimerTask implements TimerTask { private final ChannelProvider channelProvider; + private final HashedWheelTimer hashedWheelTimer; private final Long tick; protected volatile boolean cancel = false; - AbstractTimerTask(ChannelProvider channelProvider, Long tick) { - if (channelProvider == null || tick == null) { + private volatile Timeout timeout; + + AbstractTimerTask(ChannelProvider channelProvider, HashedWheelTimer hashedWheelTimer, Long tick) { + if (channelProvider == null || hashedWheelTimer == null || tick == null) { throw new IllegalArgumentException(); } - this.tick = tick; this.channelProvider = channelProvider; + this.hashedWheelTimer = hashedWheelTimer; + this.tick = tick; + start(); } static Long lastRead(Channel channel) { @@ -56,12 +61,17 @@ public abstract class AbstractTimerTask implements TimerTask { return System.currentTimeMillis(); } - public void cancel() { - this.cancel = true; + private void start() { + this.timeout = hashedWheelTimer.newTimeout(this, tick, TimeUnit.MILLISECONDS); } - private void reput(Timeout timeout, Long tick) { - if (timeout == null || tick == null) { + public synchronized void cancel() { + this.cancel = true; + this.timeout.cancel(); + } + + private synchronized void reput(Timeout timeout) { + if (timeout == null) { throw new IllegalArgumentException(); } @@ -69,24 +79,22 @@ public abstract class AbstractTimerTask implements TimerTask { return; } - Timer timer = timeout.timer(); - if (timer.isStop() || timeout.isCancelled()) { + if (hashedWheelTimer.isStop() || timeout.isCancelled()) { return; } - timer.newTimeout(timeout.task(), tick, TimeUnit.MILLISECONDS); + this.timeout = hashedWheelTimer.newTimeout(timeout.task(), tick, TimeUnit.MILLISECONDS); } @Override - public void run(Timeout timeout) throws Exception { - Collection c = channelProvider.getChannels(); - for (Channel channel : c) { - if (channel.isClosed()) { - continue; + public synchronized void run(Timeout timeout) throws Exception { + Collection channels = channelProvider.getChannels(); + for (Channel channel : channels) { + if (!channel.isClosed()) { + doTask(channel); } - doTask(channel); } - reput(timeout, tick); + reput(timeout); } protected abstract void doTask(Channel channel); diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/CloseTimerTask.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/CloseTimerTask.java index a1c850f619..7c2eea2289 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/CloseTimerTask.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/CloseTimerTask.java @@ -19,6 +19,7 @@ package org.apache.dubbo.remoting.exchange.support.header; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.remoting.Channel; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_RESPONSE; @@ -33,8 +34,8 @@ public class CloseTimerTask extends AbstractTimerTask { private final int idleTimeout; - public CloseTimerTask(ChannelProvider channelProvider, Long heartbeatTimeoutTick, int idleTimeout) { - super(channelProvider, heartbeatTimeoutTick); + public CloseTimerTask(ChannelProvider channelProvider, HashedWheelTimer hashedWheelTimer, Long heartbeatTimeoutTick, int idleTimeout) { + super(channelProvider, hashedWheelTimer, heartbeatTimeoutTick); this.idleTimeout = idleTimeout; } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java index b25951a98c..f776e9e5d7 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java @@ -19,7 +19,6 @@ package org.apache.dubbo.remoting.exchange.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.resource.GlobalResourceInitializer; import org.apache.dubbo.common.timer.HashedWheelTimer; -import org.apache.dubbo.common.timer.Timeout; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.remoting.ChannelHandler; @@ -51,12 +50,13 @@ public class HeaderExchangeClient implements ExchangeClient { private final ExchangeChannel channel; public static GlobalResourceInitializer IDLE_CHECK_TIMER = new GlobalResourceInitializer<>(() -> - new HashedWheelTimer(new NamedThreadFactory("dubbo-client-idleCheck", true), 1, + new HashedWheelTimer(new NamedThreadFactory("dubbo-client-heartbeat-reconnect", true), 1, TimeUnit.SECONDS, TICKS_PER_WHEEL), HashedWheelTimer::stop); - private Timeout reconnectTimer; - private Timeout heartBeatTimer; + private ReconnectTimerTask reconnectTimerTask; + private HeartbeatTimerTask heartBeatTimerTask; + private final int idleTimeout; public HeaderExchangeClient(Client client, boolean startTimer) { Assert.notNull(client, "Client can't be null"); @@ -65,8 +65,11 @@ public class HeaderExchangeClient implements ExchangeClient { if (startTimer) { URL url = client.getUrl(); + idleTimeout = getIdleTimeout(url); startReconnectTask(url); startHeartBeatTask(url); + } else { + idleTimeout = 0; } } @@ -107,7 +110,16 @@ public class HeaderExchangeClient implements ExchangeClient { @Override public boolean isConnected() { - return channel.isConnected(); + if (channel.isConnected()) { + if (idleTimeout <= 0) { + return true; + } + Long lastRead = (Long) channel.getAttribute(HeartbeatHandler.KEY_READ_TIMESTAMP); + Long now = System.currentTimeMillis(); + + return lastRead == null || now - lastRead < idleTimeout; + } + return false; } @Override @@ -136,7 +148,7 @@ public class HeaderExchangeClient implements ExchangeClient { } @Override - public void close() { + public synchronized void close() { doClose(); channel.close(); } @@ -167,7 +179,7 @@ public class HeaderExchangeClient implements ExchangeClient { } @Override - public void reconnect() throws RemotingException { + public synchronized void reconnect() throws RemotingException { client.reconnect(); } @@ -193,32 +205,27 @@ public class HeaderExchangeClient implements ExchangeClient { private void startHeartBeatTask(URL url) { if (!client.canHandleIdle()) { - AbstractTimerTask.ChannelProvider cp = () -> Collections.singletonList(HeaderExchangeClient.this); int heartbeat = getHeartbeat(url); long heartbeatTick = calculateLeastDuration(heartbeat); - HeartbeatTimerTask heartBeatTimerTask = new HeartbeatTimerTask(cp, heartbeatTick, heartbeat); - heartBeatTimer = IDLE_CHECK_TIMER.get().newTimeout(heartBeatTimerTask, heartbeatTick, TimeUnit.MILLISECONDS); + heartBeatTimerTask = new HeartbeatTimerTask(() -> Collections.singleton(this), IDLE_CHECK_TIMER.get(), heartbeatTick, heartbeat); } } private void startReconnectTask(URL url) { if (shouldReconnect(url)) { - AbstractTimerTask.ChannelProvider cp = () -> Collections.singletonList(HeaderExchangeClient.this); - int idleTimeout = getIdleTimeout(url); long heartbeatTimeoutTick = calculateLeastDuration(idleTimeout); - ReconnectTimerTask reconnectTimerTask = new ReconnectTimerTask(cp, heartbeatTimeoutTick, idleTimeout); - reconnectTimer = IDLE_CHECK_TIMER.get().newTimeout(reconnectTimerTask, heartbeatTimeoutTick, TimeUnit.MILLISECONDS); + reconnectTimerTask = new ReconnectTimerTask(() -> Collections.singleton(this), IDLE_CHECK_TIMER.get(), heartbeatTimeoutTick, idleTimeout); } } private void doClose() { - if (heartBeatTimer != null) { - heartBeatTimer.cancel(); - heartBeatTimer = null; + if (heartBeatTimerTask != null) { + heartBeatTimerTask.cancel(); + heartBeatTimerTask = null; } - if (reconnectTimer != null) { - reconnectTimer.cancel(); - reconnectTimer = null; + if (reconnectTimerTask != null) { + reconnectTimerTask.cancel(); + reconnectTimerTask = null; } } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java index 25d2370d4f..f27aae80e8 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java @@ -22,7 +22,6 @@ import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.resource.GlobalResourceInitializer; import org.apache.dubbo.common.timer.HashedWheelTimer; -import org.apache.dubbo.common.timer.Timeout; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NamedThreadFactory; @@ -44,9 +43,9 @@ import java.util.concurrent.atomic.AtomicBoolean; import static java.util.Collections.unmodifiableCollection; import static org.apache.dubbo.common.constants.CommonConstants.READONLY_EVENT; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_CLOSE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RESPONSE; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.remoting.Constants.HEARTBEAT_CHECK_TICK; import static org.apache.dubbo.remoting.Constants.LEAST_HEARTBEAT_DURATION; import static org.apache.dubbo.remoting.Constants.TICKS_PER_WHEEL; @@ -66,7 +65,7 @@ public class HeaderExchangeServer implements ExchangeServer { public static GlobalResourceInitializer IDLE_CHECK_TIMER = new GlobalResourceInitializer<>(() -> new HashedWheelTimer(new NamedThreadFactory("dubbo-server-idleCheck", true), 1, TimeUnit.SECONDS, TICKS_PER_WHEEL), HashedWheelTimer::stop); - private Timeout closeTimer; + private CloseTimerTask closeTimer; public HeaderExchangeServer(RemotingServer server) { Assert.notNull(server, "server == null"); @@ -141,7 +140,7 @@ public class HeaderExchangeServer implements ExchangeServer { } } catch (RemotingException e) { if (closed.get() && e.getCause() instanceof ClosedChannelException) { - // ignore ClosedChannelException which means the connection has been closed. + // ignore ClosedChannelException which means the connection has been closed. continue; } logger.warn(TRANSPORT_FAILED_RESPONSE, "", "", "send cannot write message error.", e); @@ -265,10 +264,7 @@ public class HeaderExchangeServer implements ExchangeServer { AbstractTimerTask.ChannelProvider cp = () -> unmodifiableCollection(HeaderExchangeServer.this.getChannels()); int idleTimeout = getIdleTimeout(url); long idleTimeoutTick = calculateLeastDuration(idleTimeout); - CloseTimerTask closeTimerTask = new CloseTimerTask(cp, idleTimeoutTick, idleTimeout); - - // init task and start timer. - this.closeTimer = IDLE_CHECK_TIMER.get().newTimeout(closeTimerTask, idleTimeoutTick, TimeUnit.MILLISECONDS); + this.closeTimer = new CloseTimerTask(cp, IDLE_CHECK_TIMER.get(), idleTimeoutTick, idleTimeout); } } } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatTimerTask.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatTimerTask.java index 79e2efe876..e418a1de99 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatTimerTask.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatTimerTask.java @@ -20,6 +20,7 @@ package org.apache.dubbo.remoting.exchange.support.header; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.exchange.Request; @@ -35,8 +36,8 @@ public class HeartbeatTimerTask extends AbstractTimerTask { private final int heartbeat; - HeartbeatTimerTask(ChannelProvider channelProvider, Long heartbeatTick, int heartbeat) { - super(channelProvider, heartbeatTick); + HeartbeatTimerTask(ChannelProvider channelProvider, HashedWheelTimer hashedWheelTimer, Long heartbeatTick, int heartbeat) { + super(channelProvider, hashedWheelTimer, heartbeatTick); this.heartbeat = heartbeat; } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTask.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTask.java index 54a035b764..506ec003c1 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTask.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTask.java @@ -19,11 +19,12 @@ package org.apache.dubbo.remoting.exchange.support.header; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Client; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RECONNECT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RECONNECT; /** * ReconnectTimerTask @@ -34,8 +35,8 @@ public class ReconnectTimerTask extends AbstractTimerTask { private final int idleTimeout; - public ReconnectTimerTask(ChannelProvider channelProvider, Long heartbeatTimeoutTick, int idleTimeout) { - super(channelProvider, heartbeatTimeoutTick); + public ReconnectTimerTask(ChannelProvider channelProvider, HashedWheelTimer hashedWheelTimer, Long heartbeatTimeoutTick, int idleTimeout) { + super(channelProvider, hashedWheelTimer, heartbeatTimeoutTick); this.idleTimeout = idleTimeout; } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java index 0f0d079927..d109e97cb1 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java @@ -24,24 +24,21 @@ import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.ObjectInput; import org.apache.dubbo.common.serialize.ObjectOutput; import org.apache.dubbo.common.serialize.Serialization; -import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.remoting.utils.UrlUtils; import org.apache.dubbo.rpc.model.FrameworkModel; -import org.apache.dubbo.rpc.model.FrameworkServiceRepository; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import static org.apache.dubbo.common.BaseServiceMetadata.keyWithoutGroup; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_SERIALIZATION; public class CodecSupport { @@ -163,34 +160,19 @@ public class CodecSupport { return Arrays.equals(payload, getNullBytesOf(getSerializationById(proto))); } - public static void checkSerialization(FrameworkServiceRepository serviceRepository, String path, String version, Byte id) throws IOException { - List urls = serviceRepository.lookupRegisteredProviderUrlsWithoutGroup(keyWithoutGroup(path, version)); - if (CollectionUtils.isEmpty(urls)) { - throw new IOException("Service " + path + " with version " + version + " not found, invocation rejected."); - } else { - boolean match = urls.stream().anyMatch(url -> isMatch(url, id)); - if (!match) { - throw new IOException("Unexpected serialization id:" + id + " received from network, please check if the peer send the right id."); - } - } - + public static void checkSerialization(String requestSerializeName, URL url) throws IOException { + Collection all = UrlUtils.allSerializations(url); + checkSerialization(requestSerializeName, all); } - /** - * Is Match - * - * @param url url - * @param id id - * @return boolean - */ - private static boolean isMatch(URL url, Byte id) { - Byte localId; - for (String serialization : UrlUtils.allSerializations(url)) { - localId = SERIALIZATIONNAME_ID_MAP.get(serialization); - if (id.equals(localId)) { - return true; + public static void checkSerialization(String requestSerializeName, Collection allSerializeName) throws IOException { + for (String serialization : allSerializeName) { + if (serialization.equals(requestSerializeName)) { + return; } } - return false; + throw new IOException("Unexpected serialization type:" + requestSerializeName + " received from network, please check if the peer send the right id."); } + + } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelHandlers.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelHandlers.java index 42fdcaf3dd..f11163515a 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelHandlers.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelHandlers.java @@ -33,7 +33,7 @@ public class ChannelHandlers { return ChannelHandlers.getInstance().wrapInternal(handler, url); } - protected static ChannelHandlers getInstance() { + public static ChannelHandlers getInstance() { return INSTANCE; } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/utils/UrlUtils.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/utils/UrlUtils.java index 983b5b1927..56b8bac3a9 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/utils/UrlUtils.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/utils/UrlUtils.java @@ -34,6 +34,8 @@ import static org.apache.dubbo.remoting.Constants.PREFER_SERIALIZATION_KEY; import static org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY; public class UrlUtils { + private static final String ALLOWED_SERIALIZATION_KEY = "allowedSerialization"; + public static int getIdleTimeout(URL url) { int heartBeat = getHeartbeat(url); // idleTimeout should be at least more than twice heartBeat because possible retries of client. @@ -91,6 +93,7 @@ public class UrlUtils { * @param url url * @return {@link List}<{@link String}> */ + @SuppressWarnings("unchecked") public static Collection allSerializations(URL url) { // preferSerialization -> serialization -> default serialization Set serializations = new LinkedHashSet<>(preferSerialization(url)); diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java index bc40fae681..09e1f6a341 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java @@ -31,12 +31,15 @@ import org.apache.dubbo.remoting.buffer.ChannelBuffers; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.exchange.codec.ExchangeCodec; +import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import org.apache.dubbo.remoting.telnet.codec.TelnetCodec; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -140,6 +143,8 @@ class ExchangeCodecTest extends TelnetCodecTest { @Test void test_Decode_Error_Length() throws IOException { + DefaultFuture future = DefaultFuture.newFuture(Mockito.mock(Channel.class), new Request(0), 100000, null); + byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Person person = new Person(); byte[] request = getRequestBytes(person, header); @@ -151,6 +156,8 @@ class ExchangeCodecTest extends TelnetCodecTest { Assertions.assertEquals(person, obj.getResult()); //only decode necessary bytes Assertions.assertEquals(request.length, buffer.readerIndex()); + + future.cancel(); } @Test @@ -229,6 +236,8 @@ class ExchangeCodecTest extends TelnetCodecTest { @Test void test_Decode_Return_Response_Person() throws IOException { + DefaultFuture future = DefaultFuture.newFuture(Mockito.mock(Channel.class), new Request(0), 100000, null); + //00000010-response/oneway/hearbeat=false/hessian |20-stats=ok|id=0|length=0 byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Person person = new Person(); @@ -238,6 +247,8 @@ class ExchangeCodecTest extends TelnetCodecTest { Assertions.assertEquals(20, obj.getStatus()); Assertions.assertEquals(person, obj.getResult()); System.out.println(obj); + + future.cancel(); } @Test //The status input has a problem, and the read information is wrong when the serialization is serialized. @@ -251,6 +262,7 @@ class ExchangeCodecTest extends TelnetCodecTest { } @Test + @Disabled("Event should not be object.") void test_Decode_Return_Request_Event_Object() throws IOException { //|10011111|20-stats=ok|id=0|length=0 byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) (SERIALIZATION_BYTE | (byte) 0xe0), 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -296,6 +308,7 @@ class ExchangeCodecTest extends TelnetCodecTest { } @Test + @Disabled("Event should not be object.") void test_Decode_Return_Request_Object() throws IOException { //|10011111|20-stats=ok|id=0|length=0 byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) (SERIALIZATION_BYTE | (byte) 0xe0), 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -329,6 +342,8 @@ class ExchangeCodecTest extends TelnetCodecTest { @Test void test_Header_Response_NoSerializationFlag() throws IOException { + DefaultFuture future = DefaultFuture.newFuture(Mockito.mock(Channel.class), new Request(0), 100000, null); + //00000010-response/oneway/hearbeat=false/noset |20-stats=ok|id=0|length=0 byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Person person = new Person(); @@ -338,10 +353,14 @@ class ExchangeCodecTest extends TelnetCodecTest { Assertions.assertEquals(20, obj.getStatus()); Assertions.assertEquals(person, obj.getResult()); System.out.println(obj); + + future.cancel(); } @Test void test_Header_Response_Heartbeat() throws IOException { + DefaultFuture future = DefaultFuture.newFuture(Mockito.mock(Channel.class), new Request(0), 100000, null); + //00000010-response/oneway/hearbeat=true |20-stats=ok|id=0|length=0 byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Person person = new Person(); @@ -351,6 +370,8 @@ class ExchangeCodecTest extends TelnetCodecTest { Assertions.assertEquals(20, obj.getStatus()); Assertions.assertEquals(person, obj.getResult()); System.out.println(obj); + + future.cancel(); } @Test @@ -375,7 +396,10 @@ class ExchangeCodecTest extends TelnetCodecTest { } @Test + @Disabled("Event should not be object.") void test_Encode_Response() throws IOException { + DefaultFuture future = DefaultFuture.newFuture(Mockito.mock(Channel.class), new Request(1001), 100000, null); + ChannelBuffer encodeBuffer = ChannelBuffers.dynamicBuffer(1024); Channel channel = getClientSideChannel(url); Response response = new Response(); @@ -401,6 +425,7 @@ class ExchangeCodecTest extends TelnetCodecTest { // encode response verson ?? // Assertions.assertEquals(response.getProtocolVersion(), obj.getVersion()); + future.cancel(); } @Test diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java index 987ec388f2..be487e2de0 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java @@ -134,8 +134,6 @@ class DefaultFutureTest { Channel channel = new MockedChannel(); int channelId = 10; Request request = new Request(channelId); - ExecutorService sharedExecutor = ExtensionLoader.getExtensionLoader(ExecutorRepository.class) - .getDefaultExtension().createExecutorIfAbsent(URL.valueOf("dubbo://127.0.0.1:23456")); ThreadlessExecutor executor = new ThreadlessExecutor(); DefaultFuture f = DefaultFuture.newFuture(channel, request, 1000, executor); //mark the future is sent @@ -143,11 +141,15 @@ class DefaultFutureTest { // get operate will throw a interrupted exception, because the thread is interrupted. try { new InterruptThread(Thread.currentThread()).start(); - executor.waitAndDrain(); + while (!f. isDone()){ + executor.waitAndDrain(); + } f.get(); } catch (Exception e) { Assertions.assertTrue(e instanceof InterruptedException, "catch exception is not interrupted exception!"); System.out.println(e.getMessage()); + } finally { + executor.shutdown(); } //waiting timeout check task finished Thread.sleep(1500); diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/CloseTimerTaskTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/CloseTimerTaskTest.java index f9d232575d..882a481c24 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/CloseTimerTaskTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/CloseTimerTaskTest.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.remoting.Channel; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -54,7 +55,12 @@ class CloseTimerTaskTest { }; AbstractTimerTask.ChannelProvider cp = () -> Collections.singletonList(channel); - closeTimerTask = new CloseTimerTask(cp, tickDuration / HEARTBEAT_CHECK_TICK, (int) tickDuration); + closeTimerTask = new CloseTimerTask(cp, closeTimer, tickDuration / HEARTBEAT_CHECK_TICK, (int) tickDuration); + } + + @AfterEach + public void teardown() { + closeTimerTask.cancel(); } @Test diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartBeatTaskTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartBeatTaskTest.java index cb07c11df7..07a0f63a0a 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartBeatTaskTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartBeatTaskTest.java @@ -19,9 +19,9 @@ package org.apache.dubbo.remoting.exchange.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.timer.HashedWheelTimer; -import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.exchange.Request; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -55,8 +55,12 @@ class HeartBeatTaskTest { } }; - AbstractTimerTask.ChannelProvider cp = () -> Collections.singletonList(channel); - heartbeatTimerTask = new HeartbeatTimerTask(cp, tickDuration / HEARTBEAT_CHECK_TICK, (int) tickDuration); + heartbeatTimerTask = new HeartbeatTimerTask(() -> Collections.singleton(channel), heartbeatTimer, tickDuration / HEARTBEAT_CHECK_TICK, (int) tickDuration); + } + + @AfterEach + public void teardown() { + heartbeatTimerTask.cancel(); } @Test @@ -67,8 +71,6 @@ class HeartBeatTaskTest { channel.setAttribute(HeartbeatHandler.KEY_READ_TIMESTAMP, now); channel.setAttribute(HeartbeatHandler.KEY_WRITE_TIMESTAMP, now); - heartbeatTimer.newTimeout(heartbeatTimerTask, 250, TimeUnit.MILLISECONDS); - Thread.sleep(2000L); List objects = channel.getSentObjects(); Assertions.assertTrue(objects.size() > 0); diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTaskTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTaskTest.java index 5b8f30006d..e68b26355e 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTaskTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTaskTest.java @@ -18,8 +18,8 @@ package org.apache.dubbo.remoting.exchange.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.timer.HashedWheelTimer; -import org.apache.dubbo.remoting.Channel; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -56,8 +56,12 @@ class ReconnectTimerTaskTest { } }; - AbstractTimerTask.ChannelProvider cp = () -> Collections.singletonList(channel); - reconnectTimerTask = new ReconnectTimerTask(cp, tickDuration / HEARTBEAT_CHECK_TICK, (int) tickDuration); + reconnectTimerTask = new ReconnectTimerTask(() -> Collections.singleton(channel), reconnectTimer, tickDuration / HEARTBEAT_CHECK_TICK, (int) tickDuration); + } + + @AfterEach + public void teardown() { + reconnectTimerTask.cancel(); } @Test @@ -68,8 +72,6 @@ class ReconnectTimerTaskTest { channel.setAttribute(HeartbeatHandler.KEY_READ_TIMESTAMP, now - 1000); channel.setAttribute(HeartbeatHandler.KEY_WRITE_TIMESTAMP, now - 1000); - reconnectTimer.newTimeout(reconnectTimerTask, 250, TimeUnit.MILLISECONDS); - Thread.sleep(2000L); Assertions.assertTrue(channel.getReconnectCount() > 0); isConnected = true; diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/resources/security/serialize.allowlist b/dubbo-remoting/dubbo-remoting-api/src/test/resources/security/serialize.allowlist new file mode 100644 index 0000000000..74b8dcef99 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/resources/security/serialize.allowlist @@ -0,0 +1,20 @@ +# +# +# 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. +# +# + +org.apache.dubbo diff --git a/dubbo-remoting/dubbo-remoting-http/pom.xml b/dubbo-remoting/dubbo-remoting-http/pom.xml index b2ef9653a4..e184f53eed 100644 --- a/dubbo-remoting/dubbo-remoting-http/pom.xml +++ b/dubbo-remoting/dubbo-remoting-http/pom.xml @@ -76,6 +76,5 @@ httpclient - diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpHandler.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpHandler.java index b05e065341..e6e9203c89 100644 --- a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpHandler.java +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpHandler.java @@ -16,15 +16,12 @@ */ package org.apache.dubbo.remoting.http; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * http invocation handler. */ -public interface HttpHandler { +public interface HttpHandler { /** * invoke. @@ -32,8 +29,7 @@ public interface HttpHandler { * @param request request. * @param response response. * @throws IOException - * @throws ServletException */ - void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException; + void handle(REQUEST request, RESPONSE response) throws IOException; } diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java index e5d644c90b..6d19f1636b 100644 --- a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java @@ -47,6 +47,7 @@ public class RequestTemplate implements Serializable { private String protocol = "http://"; private final Invocation invocation; private String contextPath = ""; + private Class bodyType; public RequestTemplate(Invocation invocation, String httpMethod, String address) { @@ -99,7 +100,7 @@ public class RequestTemplate implements Serializable { } } - return queryBuilder.toString(); + return queryBuilder.toString().replace("?&", "?"); } @@ -131,8 +132,9 @@ public class RequestTemplate implements Serializable { return getUnSerializedBody() == null; } - public RequestTemplate body(Object body) { + public RequestTemplate body(Object body,Class bodyType) { this.body = body; + setBodyType(bodyType); return this; } @@ -224,7 +226,7 @@ public class RequestTemplate implements Serializable { if (params == null) { params = new HashSet<>(); - this.headers.put(key, params); + this.queries.put(key, params); } params.addAll(values); } @@ -300,4 +302,12 @@ public class RequestTemplate implements Serializable { public void setContextPath(String contextPath) { this.contextPath = contextPath; } + + public Class getBodyType() { + return bodyType; + } + + public void setBodyType(Class bodyType) { + this.bodyType = bodyType; + } } diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.java index 1cca829291..bb057675df 100644 --- a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.java +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.java @@ -20,6 +20,9 @@ import java.io.IOException; import java.util.List; import java.util.Map; +/** + * rest response facade + */ public interface RestResult { String getContentType(); @@ -32,4 +35,8 @@ public interface RestResult { int getResponseCode() throws IOException; String getMessage() throws IOException; + + default String appendErrorMessage(String message, String errorInfo) { + return message + "\n error info is: " + errorInfo; + } } diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java index 9895eab46a..0344a4aab9 100644 --- a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java @@ -23,11 +23,19 @@ import org.apache.dubbo.remoting.http.config.HttpClientConfig; import org.apache.commons.io.IOUtils; import org.apache.http.Header; +import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpOptions; +import org.apache.http.client.methods.HttpPatch; import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.methods.HttpTrace; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -58,14 +66,13 @@ public class HttpClientRestClient implements RestClient { HttpRequestBase httpRequest = null; String httpMethod = requestTemplate.getHttpMethod(); - if ("GET".equals(httpMethod)) { - httpRequest = new HttpGet(requestTemplate.getURL()); - } else if ("POST".equals(httpMethod)) { - HttpPost httpPost = new HttpPost(requestTemplate.getURL()); - httpPost.setEntity(new ByteArrayEntity(requestTemplate.getSerializedBody())); - httpRequest = httpPost; + httpRequest = createHttpUriRequest(httpMethod, requestTemplate); + + if (httpRequest instanceof HttpEntityEnclosingRequest) { + ((HttpEntityEnclosingRequestBase) httpRequest).setEntity(new ByteArrayEntity(requestTemplate.getSerializedBody())); } + Map> allHeaders = requestTemplate.getAllHeaders(); allHeaders.remove("Content-Length"); @@ -91,6 +98,9 @@ public class HttpClientRestClient implements RestClient { @Override public byte[] getBody() throws IOException { + if (response.getEntity() == null) { + return new byte[0]; + } return IOUtils.toByteArray(response.getEntity().getContent()); } @@ -111,7 +121,8 @@ public class HttpClientRestClient implements RestClient { @Override public String getMessage() throws IOException { - return response.getStatusLine().getReasonPhrase(); + return appendErrorMessage(response.getStatusLine().getReasonPhrase(), + new String(getErrorResponse())); } }); } catch (IOException e) { @@ -150,4 +161,31 @@ public class HttpClientRestClient implements RestClient { PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); return HttpClients.custom().setConnectionManager(connectionManager).build(); } + + protected HttpRequestBase createHttpUriRequest(String httpMethod, RequestTemplate requestTemplate) { + String uri = requestTemplate.getURL(); + HttpRequestBase httpUriRequest = null; + if (HttpGet.METHOD_NAME.equals(httpMethod)) { + httpUriRequest = new HttpGet(uri); + } else if (HttpHead.METHOD_NAME.equals(httpMethod)) { + httpUriRequest = new HttpHead(uri); + } else if (HttpPost.METHOD_NAME.equals(httpMethod)) { + httpUriRequest = new HttpPost(uri); + } else if (HttpPut.METHOD_NAME.equals(httpMethod)) { + httpUriRequest = new HttpPut(uri); + } else if (HttpPatch.METHOD_NAME.equals(httpMethod)) { + httpUriRequest = new HttpPatch(uri); + } else if (HttpDelete.METHOD_NAME.equals(httpMethod)) { + httpUriRequest = new HttpDelete(uri); + } else if (HttpOptions.METHOD_NAME.equals(httpMethod)) { + httpUriRequest = new HttpOptions(uri); + } else if (HttpTrace.METHOD_NAME.equals(httpMethod)) { + httpUriRequest = new HttpTrace(uri); + } else { + throw new IllegalArgumentException("Invalid HTTP method: " + httpMethod); + } + return httpUriRequest; + + } + } diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java index bb98aa32ec..461d2bcd62 100644 --- a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java @@ -37,7 +37,7 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; - +// TODO add version 4.0 implements ,and default version is < 4.0,for dependency conflict public class OKHttpRestClient implements RestClient { private final OkHttpClient okHttpClient; private final HttpClientConfig httpClientConfig; @@ -56,18 +56,26 @@ public class OKHttpRestClient implements RestClient { Map> allHeaders = requestTemplate.getAllHeaders(); + boolean hasBody = false; + RequestBody requestBody = null; + // GET & HEAD body is forbidden + if (HttpMethod.permitsRequestBody(requestTemplate.getHttpMethod())) { + requestBody = RequestBody.create(null, requestTemplate.getSerializedBody()); + hasBody = true; + } + // header for (String headerName : allHeaders.keySet()) { Collection headerValues = allHeaders.get(headerName); - + if (!hasBody && "Content-Length".equals(headerName)) { + continue; + } for (String headerValue : headerValues) { + builder.addHeader(headerName, headerValue); } } - RequestBody requestBody = null; - if (HttpMethod.permitsRequestBody(requestTemplate.getHttpMethod())) { - requestBody = RequestBody.create(null, requestTemplate.getSerializedBody()); - } + builder.method(requestTemplate.getHttpMethod(), requestBody); CompletableFuture future = new CompletableFuture<>(); @@ -109,7 +117,7 @@ public class OKHttpRestClient implements RestClient { @Override public String getMessage() throws IOException { - return response.message(); + return appendErrorMessage(response.message(), new String(getBody())); } }); } diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java index b067228c44..960912c0ea 100644 --- a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java @@ -50,10 +50,10 @@ public class URLConnectionRestClient implements RestClient { HttpURLConnection connection = (HttpURLConnection) new URL(requestTemplate.getURL()).openConnection(); connection.setConnectTimeout(clientConfig.getConnectTimeout()); connection.setReadTimeout(clientConfig.getReadTimeout()); - connection.setAllowUserInteraction(false); - connection.setInstanceFollowRedirects(true); connection.setRequestMethod(requestTemplate.getHttpMethod()); + prepareConnection(connection, requestTemplate.getHttpMethod()); + // writeHeaders for (String field : requestTemplate.getAllHeaders().keySet()) { @@ -68,37 +68,7 @@ public class URLConnectionRestClient implements RestClient { boolean gzipEncodedRequest = requestTemplate.isGzipEncodedRequest(); boolean deflateEncodedRequest = requestTemplate.isDeflateEncodedRequest(); if (requestTemplate.isBodyEmpty()) { - future.complete(new RestResult() { - @Override - public String getContentType() { - return connection.getContentType(); - } - - @Override - public byte[] getBody() throws IOException { - return IOUtils.toByteArray(connection.getInputStream()); - } - - @Override - public Map> headers() { - return connection.getHeaderFields(); - } - - @Override - public byte[] getErrorResponse() throws IOException { - return IOUtils.toByteArray(connection.getErrorStream()); - } - - @Override - public int getResponseCode() throws IOException { - return connection.getResponseCode(); - } - - @Override - public String getMessage() throws IOException { - return connection.getResponseMessage(); - } - }); + future.complete(getRestResultFromConnection(connection)); return future; } Integer contentLength = requestTemplate.getContentLength(); @@ -108,7 +78,6 @@ public class URLConnectionRestClient implements RestClient { } else { connection.setChunkedStreamingMode(clientConfig.getChunkLength()); } - connection.setDoOutput(true); OutputStream out = connection.getOutputStream(); if (gzipEncodedRequest) { @@ -125,37 +94,7 @@ public class URLConnectionRestClient implements RestClient { } } - future.complete(new RestResult() { - @Override - public String getContentType() { - return connection.getContentType(); - } - - @Override - public byte[] getBody() throws IOException { - return IOUtils.toByteArray(connection.getInputStream()); - } - - @Override - public Map> headers() { - return connection.getHeaderFields(); - } - - @Override - public byte[] getErrorResponse() throws IOException { - return IOUtils.toByteArray(connection.getErrorStream()); - } - - @Override - public int getResponseCode() throws IOException { - return connection.getResponseCode(); - } - - @Override - public String getMessage() throws IOException { - return connection.getResponseMessage(); - } - }); + future.complete(getRestResultFromConnection(connection)); } catch (Exception e) { future.completeExceptionally(e); } @@ -178,4 +117,63 @@ public class URLConnectionRestClient implements RestClient { return true; } + + private RestResult getRestResultFromConnection(HttpURLConnection connection) { + + return new RestResult() { + @Override + public String getContentType() { + return connection.getContentType(); + } + + @Override + public byte[] getBody() throws IOException { + return IOUtils.toByteArray(connection.getInputStream()); + } + + @Override + public Map> headers() { + return connection.getHeaderFields(); + } + + @Override + public byte[] getErrorResponse() throws IOException { + return IOUtils.toByteArray(connection.getErrorStream()); + } + + @Override + public int getResponseCode() throws IOException { + return connection.getResponseCode(); + } + + @Override + public String getMessage() throws IOException { + return appendErrorMessage(connection.getResponseMessage(), new String(getErrorResponse())); + + } + }; + } + + private void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException { + + + connection.setDoInput(true); + + if ("GET".equals(httpMethod)) { + connection.setInstanceFollowRedirects(true); + } else { + connection.setInstanceFollowRedirects(false); + } + + + if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) || + "PATCH".equals(httpMethod) || "DELETE".equals(httpMethod)) { + connection.setDoOutput(true); + } else { + connection.setDoOutput(false); + } + + connection.setRequestMethod(httpMethod); + } + } diff --git a/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinderTest.java b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinderTest.java index 16f7bfd799..a00fbcd9c2 100644 --- a/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinderTest.java +++ b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinderTest.java @@ -39,7 +39,7 @@ class JettyHttpBinderTest { int port = NetUtils.getAvailablePort(); URL url = new ServiceConfigURL("http", "localhost", port, new String[]{Constants.BIND_PORT_KEY, String.valueOf(port)}); - HttpServer httpServer = new JettyHttpServer(url, new HttpHandler() { + HttpServer httpServer = new JettyHttpServer(url, new HttpHandler() { @Override public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException { response.getWriter().write("Jetty"); diff --git a/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapterTest.java b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapterTest.java index 8557f32e45..f2de8ccdef 100644 --- a/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapterTest.java +++ b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapterTest.java @@ -49,7 +49,7 @@ class JettyLoggerAdapterTest { int port = NetUtils.getAvailablePort(); URL url = new ServiceConfigURL("http", "localhost", port, new String[]{Constants.BIND_PORT_KEY, String.valueOf(port)}); - HttpServer httpServer = new JettyHttpServer(url, new HttpHandler() { + HttpServer httpServer = new JettyHttpServer(url, new HttpHandler() { @Override public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException { response.getWriter().write("Jetty is using Dubbo's logger"); diff --git a/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/rest/RestClientTest.java b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/rest/RestClientTest.java new file mode 100644 index 0000000000..04b4489b5b --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/rest/RestClientTest.java @@ -0,0 +1,199 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting.http.rest; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.url.component.ServiceConfigURL; +import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.remoting.http.*; +import org.apache.dubbo.remoting.http.config.HttpClientConfig; +import org.apache.dubbo.remoting.http.jetty.JettyHttpServer; +import org.apache.dubbo.remoting.http.restclient.HttpClientRestClient; +import org.apache.dubbo.remoting.http.restclient.OKHttpRestClient; +import org.apache.dubbo.remoting.http.restclient.URLConnectionRestClient; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +public class RestClientTest { + + @Test + public void testRestClient() throws Exception { + int port = NetUtils.getAvailablePort(); + URL url = new ServiceConfigURL("http", "localhost", port, + new String[]{Constants.BIND_PORT_KEY, String.valueOf(port)}); + HttpServer httpServer = new JettyHttpServer(url, new HttpHandler() { + @Override + public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException { + response.getWriter().write("Jetty"); + } + }); + + RequestTemplate requestTemplate = new RequestTemplate(null, "POST", "localhost:" + port); + + requestTemplate.addParam("p1", "value1"); + requestTemplate.addParam("p2", "value2"); + + requestTemplate.addParams("p3", Arrays.asList("value3", "value3.1")); + requestTemplate.addHeader("test", "dubbo"); + requestTemplate.addKeepAliveHeader(60); + + requestTemplate.addHeaders("header", Arrays.asList("h1", "h2")); + + requestTemplate.path("/test"); + requestTemplate.serializeBody("test".getBytes(StandardCharsets.UTF_8)); + + RestClient restClient = new OKHttpRestClient(new HttpClientConfig()); + + CompletableFuture send = restClient.send(requestTemplate); + + RestResult restResult = send.get(); + + + assertThat(new String(restResult.getBody()), is("Jetty")); + + + restClient = new HttpClientRestClient(new HttpClientConfig()); + + send = restClient.send(requestTemplate); + + restResult = send.get(); + + assertThat(new String(restResult.getBody()), is("Jetty")); + + restClient = new URLConnectionRestClient(new HttpClientConfig()); + + send = restClient.send(requestTemplate); + + restResult = send.get(); + + assertThat(new String(restResult.getBody()), is("Jetty")); + + httpServer.close(); + } + + + @Test + public void testError() throws Exception { + int port = NetUtils.getAvailablePort(); + URL url = new ServiceConfigURL("http", "localhost", port, + new String[]{Constants.BIND_PORT_KEY, String.valueOf(port)}); + HttpServer httpServer = new JettyHttpServer(url, new HttpHandler() { + @Override + public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException { + response.setStatus(500); + response.getWriter().write("server error"); + response.addHeader("Content-Type", "text/html"); + } + }); + + RequestTemplate requestTemplate = new RequestTemplate(null, null, null); + + requestTemplate.httpMethod("POST"); + requestTemplate.setAddress("localhost:" + port); + requestTemplate.setProtocol("http://"); + requestTemplate.addHeader("test", "dubbo"); + requestTemplate.path("/test"); + requestTemplate.serializeBody("test".getBytes(StandardCharsets.UTF_8)); + + RestClient restClient = new OKHttpRestClient(new HttpClientConfig()); + + CompletableFuture send = restClient.send(requestTemplate); + + String error = "Server Error\n" + + " error info is: server error"; + RestResult restResult = send.get(); + + String contentType = "text/html;charset=iso-8859-1"; + + Assertions.assertEquals(500, restResult.getResponseCode()); + Assertions.assertEquals(error, restResult.getMessage()); + Assertions.assertEquals(contentType, restResult.getContentType()); + + Map> headers = restResult.headers(); + restClient.close(); + + restClient = new HttpClientRestClient(new HttpClientConfig()); + send = restClient.send(requestTemplate); + restResult = send.get(); + + + Assertions.assertEquals(500, restResult.getResponseCode()); + Assertions.assertEquals(error, restResult.getMessage()); + Assertions.assertEquals(contentType, restResult.getContentType()); + + restClient.close(); + + + restClient = new URLConnectionRestClient(new HttpClientConfig()); + send = restClient.send(requestTemplate); + restResult = send.get(); + + Assertions.assertEquals(500, restResult.getResponseCode()); + Assertions.assertEquals(error, restResult.getMessage()); + Assertions.assertEquals(contentType, restResult.getContentType()); + restClient.close(); + + + httpServer.close(); + } + + @Test + public void testMethod() { + + RequestTemplate requestTemplate = new RequestTemplate(null, null, null); + + requestTemplate.body(new Object(), Object.class); + + Assertions.assertEquals(requestTemplate.getBodyType(),Object.class); + + + requestTemplate.addHeader("Content-Length",1); + + Integer contentLength = requestTemplate.getContentLength(); + + Assertions.assertEquals(1,contentLength); + + List strings = Arrays.asList("h1", "h2"); + + requestTemplate.addHeaders("header",strings); + + + Assertions.assertArrayEquals(strings.toArray(new String[0]),requestTemplate.getHeaders("header").toArray(new String[0])); + + strings = Arrays.asList("p1", "p2"); + + requestTemplate.addParams("param",strings); + + Assertions.assertArrayEquals(strings.toArray(new String[0]),requestTemplate.getParam("param").toArray(new String[0])); + + + + } +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinderTest.java b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinderTest.java index 10d6423963..ac100eef3f 100644 --- a/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinderTest.java +++ b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinderTest.java @@ -40,7 +40,7 @@ class TomcatHttpBinderTest { URL url = new ServiceConfigURL("http", "localhost", port, new String[]{Constants.BIND_PORT_KEY, String.valueOf(port)}); - HttpServer httpServer = new TomcatHttpBinder().bind(url, new HttpHandler() { + HttpServer httpServer = new TomcatHttpBinder().bind(url, new HttpHandler() { @Override public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException { response.getWriter().write("Tomcat"); diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java index d457f5e33a..608a530731 100644 --- a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java +++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java @@ -21,6 +21,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; @@ -30,7 +31,7 @@ import org.apache.dubbo.remoting.exchange.ExchangeHandler; import org.apache.dubbo.remoting.exchange.ExchangeServer; import org.apache.dubbo.remoting.exchange.Exchangers; import org.apache.dubbo.remoting.transport.dispatcher.FakeChannelHandlers; - +import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -38,6 +39,8 @@ import org.junit.jupiter.api.Test; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; + class HeartbeatHandlerTest { private static final Logger logger = LoggerFactory.getLogger(HeartbeatHandlerTest.class); @@ -72,6 +75,11 @@ class HeartbeatHandlerTest { .addParameter(Constants.HEARTBEAT_KEY, 1000); CountDownLatch connect = new CountDownLatch(1); CountDownLatch disconnect = new CountDownLatch(1); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + serverURL = serverURL.setScopeModel(applicationModel); TestHeartbeatHandler handler = new TestHeartbeatHandler(connect, disconnect); server = Exchangers.bind(serverURL, handler); System.out.println("Server bind successfully"); @@ -97,6 +105,11 @@ class HeartbeatHandlerTest { .addParameter(Constants.TRANSPORTER_KEY, "netty3") .addParameter(Constants.HEARTBEAT_KEY, 1000) .addParameter(Constants.CODEC_KEY, "telnet"); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + serverURL = serverURL.setScopeModel(applicationModel); CountDownLatch connect = new CountDownLatch(1); CountDownLatch disconnect = new CountDownLatch(1); TestHeartbeatHandler handler = new TestHeartbeatHandler(connect, disconnect); @@ -118,6 +131,11 @@ class HeartbeatHandlerTest { .addParameter(Constants.EXCHANGER_KEY, HeaderExchanger.NAME) .addParameter(Constants.TRANSPORTER_KEY, "netty3") .addParameter(Constants.CODEC_KEY, "telnet"); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + serverURL = serverURL.setScopeModel(applicationModel); CountDownLatch connect = new CountDownLatch(1); CountDownLatch disconnect = new CountDownLatch(1); TestHeartbeatHandler handler = new TestHeartbeatHandler(connect, disconnect); diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientReconnectTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientReconnectTest.java index a90828dbad..f4229aa56d 100644 --- a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientReconnectTest.java +++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientReconnectTest.java @@ -16,8 +16,10 @@ */ package org.apache.dubbo.remoting.transport.netty; +import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.DubboAppender; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Client; import org.apache.dubbo.remoting.Constants; @@ -25,12 +27,14 @@ import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.RemotingServer; import org.apache.dubbo.remoting.exchange.Exchangers; import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter; +import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; + /** * Client reconnect test */ @@ -71,8 +75,13 @@ class ClientReconnectTest { public Client startClient(int port, int heartbeat) throws RemotingException { - final String url = "exchange://127.0.0.1:" + port + "/client.reconnect.test?check=false&codec=exchange&client=netty3&" + - Constants.HEARTBEAT_KEY + "=" + heartbeat; + URL url = URL.valueOf("exchange://127.0.0.1:" + port + "/client.reconnect.test?check=false&codec=exchange&client=netty3&" + + Constants.HEARTBEAT_KEY + "=" + heartbeat); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + url = url.setScopeModel(applicationModel); return Exchangers.connect(url); } diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyClientTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyClientTest.java index 675a1474a0..4b14d0f4b1 100644 --- a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyClientTest.java +++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyClientTest.java @@ -18,11 +18,11 @@ package org.apache.dubbo.remoting.transport.netty; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; -import org.apache.dubbo.remoting.RemotingException; +import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.remoting.RemotingServer; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.Exchangers; - +import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -30,6 +30,8 @@ import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; + /** * Date: 5/3/11 * Time: 5:47 PM @@ -40,7 +42,13 @@ class NettyClientTest { @BeforeAll public static void setUp() throws Exception { - server = Exchangers.bind(URL.valueOf("exchange://localhost:" + port + "?server=netty3&codec=exchange"), new TelnetServerHandler()); + URL url = URL.valueOf("exchange://localhost:" + port + "?server=netty3&codec=exchange"); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + url = url.setScopeModel(applicationModel); + server = Exchangers.bind(url, new TelnetServerHandler()); } @AfterAll @@ -52,16 +60,22 @@ class NettyClientTest { } } - public static void main(String[] args) throws RemotingException, InterruptedException { - ExchangeChannel client = Exchangers.connect(URL.valueOf("exchange://10.20.153.10:20880?client=netty3&heartbeat=1000&codec=exchange")); - Thread.sleep(60 * 1000 * 50); - } +// public static void main(String[] args) throws RemotingException, InterruptedException { +// ExchangeChannel client = Exchangers.connect(URL.valueOf("exchange://10.20.153.10:20880?client=netty3&heartbeat=1000&codec=exchange")); +// Thread.sleep(60 * 1000 * 50); +// } @Test void testClientClose() throws Exception { List clients = new ArrayList(100); for (int i = 0; i < 100; i++) { - ExchangeChannel client = Exchangers.connect(URL.valueOf("exchange://localhost:" + port + "?client=netty3&codec=exchange")); + URL url = URL.valueOf("exchange://localhost:" + port + "?client=netty3&codec=exchange"); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + url = url.setScopeModel(applicationModel); + ExchangeChannel client = Exchangers.connect(url); Thread.sleep(5); clients.add(client); } @@ -74,7 +88,13 @@ class NettyClientTest { @Test void testServerClose() throws Exception { for (int i = 0; i < 100; i++) { - RemotingServer aServer = Exchangers.bind(URL.valueOf("exchange://localhost:" + NetUtils.getAvailablePort(6000) + "?server=netty3&codec=exchange"), new TelnetServerHandler()); + URL url = URL.valueOf("exchange://localhost:" + NetUtils.getAvailablePort(6000) + "?server=netty3&codec=exchange"); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + url = url.setScopeModel(applicationModel); + RemotingServer aServer = Exchangers.bind(url, new TelnetServerHandler()); aServer.close(); } } diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyClientToServerTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyClientToServerTest.java index 4a871e0fb7..57131fcbc6 100644 --- a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyClientToServerTest.java +++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyClientToServerTest.java @@ -17,12 +17,16 @@ package org.apache.dubbo.remoting.transport.netty; import org.apache.dubbo.common.URL; +import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeServer; import org.apache.dubbo.remoting.exchange.Exchangers; import org.apache.dubbo.remoting.exchange.support.Replier; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; /** * NettyClientToServerTest @@ -33,6 +37,11 @@ class NettyClientToServerTest extends ClientToServerTest { // add heartbeat cycle to avoid unstable ut. URL url = URL.valueOf("exchange://localhost:" + port + "?server=netty3&codec=exchange"); url = url.addParameter(Constants.HEARTBEAT_KEY, 600 * 1000); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + url = url.setScopeModel(applicationModel); return Exchangers.bind(url, receiver); } @@ -40,6 +49,11 @@ class NettyClientToServerTest extends ClientToServerTest { // add heartbeat cycle to avoid unstable ut. URL url = URL.valueOf("exchange://localhost:" + port + "?client=netty3&timeout=3000&codec=exchange"); url = url.addParameter(Constants.HEARTBEAT_KEY, 600 * 1000); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + url = url.setScopeModel(applicationModel); return Exchangers.connect(url); } diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyStringTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyStringTest.java index d1221f8ec0..8b1fde4034 100644 --- a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyStringTest.java +++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyStringTest.java @@ -18,14 +18,17 @@ package org.apache.dubbo.remoting.transport.netty; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeServer; import org.apache.dubbo.remoting.exchange.Exchangers; - +import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; + /** * Date: 4/26/11 * Time: 4:13 PM @@ -40,8 +43,17 @@ class NettyStringTest { //int port = 10001; int port = NetUtils.getAvailablePort(); System.out.println(port); - server = Exchangers.bind(URL.valueOf("telnet://0.0.0.0:" + port + "?server=netty3&codec=telnet"), new TelnetServerHandler()); - client = Exchangers.connect(URL.valueOf("telnet://127.0.0.1:" + port + "?client=netty3&codec=telnet"), new TelnetClientHandler()); + URL serverURL = URL.valueOf("telnet://0.0.0.0:" + port + "?server=netty3&codec=telnet"); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + serverURL = serverURL.setScopeModel(applicationModel); + + URL clientURL = URL.valueOf("telnet://127.0.0.1:" + port + "?client=netty3&codec=telnet"); + clientURL = clientURL.setScopeModel(applicationModel); + server = Exchangers.bind(serverURL, new TelnetServerHandler()); + client = Exchangers.connect(clientURL, new TelnetClientHandler()); } @AfterAll diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ThreadNameTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ThreadNameTest.java index d990649654..203cc35353 100644 --- a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ThreadNameTest.java +++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ThreadNameTest.java @@ -18,11 +18,11 @@ package org.apache.dubbo.remoting.transport.netty; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.rpc.model.ApplicationModel; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -32,6 +32,8 @@ import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; + class ThreadNameTest { private NettyServer server; @@ -53,12 +55,15 @@ class ThreadNameTest { public void before() throws Exception { int port = NetUtils.getAvailablePort(20880 + new Random().nextInt(10000)); serverURL = URL.valueOf("telnet://localhost?side=provider&codec=telnet") - .setPort(port) - .setScopeModel(ApplicationModel.defaultModel()); + .setPort(port); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + serverURL = serverURL.setScopeModel(applicationModel); clientURL = URL.valueOf("telnet://localhost?side=consumer&codec=telnet") - .setPort(port) - .setScopeModel(ApplicationModel.defaultModel()); - + .setPort(port); + clientURL = clientURL.setScopeModel(applicationModel); serverHandler = new ThreadNameVerifyHandler(serverRegex, false, serverLatch); clientHandler = new ThreadNameVerifyHandler(clientRegex, true, clientLatch); server = new NettyServer(serverURL, serverHandler); diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/resources/security/serialize.allowlist b/dubbo-remoting/dubbo-remoting-netty/src/test/resources/security/serialize.allowlist new file mode 100644 index 0000000000..74b8dcef99 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-netty/src/test/resources/security/serialize.allowlist @@ -0,0 +1,20 @@ +# +# +# 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. +# +# + +org.apache.dubbo diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyBackedChannelBuffer.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyBackedChannelBuffer.java index dc5b1b609a..dc4ef5937f 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyBackedChannelBuffer.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyBackedChannelBuffer.java @@ -450,7 +450,6 @@ public class NettyBackedChannelBuffer implements ChannelBuffer { return ChannelBuffers.compare(this, o); } - @Override public void release() { ReferenceCountUtil.safeRelease(buffer); } diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyConnectionHandler.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyConnectionHandler.java index 926110373b..3ab6fbc8fc 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyConnectionHandler.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyConnectionHandler.java @@ -77,6 +77,10 @@ public class NettyConnectionHandler extends ChannelInboundHandlerAdapter impleme LOGGER.debug(String.format("Connection %s is reconnecting, attempt=%d", connectionClient, 1)); } final EventLoop eventLoop = nettyChannel.eventLoop(); + if (connectionClient.isClosed()) { + LOGGER.info("The client has been closed and will not reconnect. "); + return; + } eventLoop.schedule(() -> { try { connectionClient.doConnect(); diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientReconnectTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientReconnectTest.java index 03918da077..369108ea6b 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientReconnectTest.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientReconnectTest.java @@ -16,8 +16,12 @@ */ package org.apache.dubbo.remoting.transport.netty4; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.DubboAppender; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Client; import org.apache.dubbo.remoting.Constants; @@ -25,12 +29,15 @@ import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.RemotingServer; import org.apache.dubbo.remoting.exchange.Exchangers; import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter; +import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; - +import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; + /** * Client reconnect test */ @@ -74,12 +81,31 @@ class ClientReconnectTest { public Client startClient(int port, int heartbeat) throws RemotingException { - final String url = "exchange://127.0.0.1:" + port + "/client.reconnect.test?client=netty4&check=false&" + Constants.HEARTBEAT_KEY + "=" + heartbeat; + URL url = URL.valueOf("exchange://127.0.0.1:" + port + "/client.reconnect.test?client=netty4&check=false&" + Constants.HEARTBEAT_KEY + "=" + heartbeat); + FrameworkModel frameworkModel = new FrameworkModel(); + ApplicationModel applicationModel = frameworkModel.newApplication(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + ConfigManager configManager = new ConfigManager(applicationModel); + configManager.setApplication(applicationConfig); + configManager.getApplication(); + applicationModel.setConfigManager(configManager); + url = url.putAttribute(CommonConstants.SCOPE_MODEL, applicationModel); return Exchangers.connect(url); } public RemotingServer startServer(int port) throws RemotingException { - final String url = "exchange://127.0.0.1:" + port + "/client.reconnect.test?server=netty4"; + URL url = URL.valueOf("exchange://127.0.0.1:" + port + "/client.reconnect.test?server=netty4"); + FrameworkModel frameworkModel = new FrameworkModel(); + ApplicationModel applicationModel = frameworkModel.newApplication(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + ConfigManager configManager = new ConfigManager(applicationModel); + configManager.setApplication(applicationConfig); + configManager.getApplication(); + applicationModel.setConfigManager(configManager); + ModuleModel moduleModel = applicationModel.getDefaultModule(); + url = url.putAttribute(CommonConstants.SCOPE_MODEL, moduleModel); return Exchangers.bind(url, new HandlerAdapter()); } diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ConnectionTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ConnectionTest.java index 0c5d8c4845..2c230d6c61 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ConnectionTest.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ConnectionTest.java @@ -17,13 +17,17 @@ package org.apache.dubbo.remoting.transport.netty4; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient; import org.apache.dubbo.remoting.api.connection.ConnectionManager; import org.apache.dubbo.remoting.api.connection.MultiplexProtocolConnectionManager; import org.apache.dubbo.remoting.api.pu.DefaultPuHandler; - +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -34,6 +38,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; + public class ConnectionTest { @@ -47,6 +53,17 @@ public class ConnectionTest { public static void init() throws RemotingException { int port = NetUtils.getAvailablePort(); url = URL.valueOf("empty://127.0.0.1:" + port + "?foo=bar"); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + ConfigManager configManager = new ConfigManager(applicationModel); + configManager.setApplication(applicationConfig); + configManager.getApplication(); + applicationModel.setConfigManager(configManager); + url = url.setScopeModel(applicationModel); + ModuleModel moduleModel = applicationModel.getDefaultModule(); + url = url.putAttribute(CommonConstants.SCOPE_MODEL, moduleModel); server = new NettyPortUnificationServer(url, new DefaultPuHandler()); server.bind(); connectionManager = url.getOrDefaultFrameworkModel().getExtensionLoader(ConnectionManager.class).getExtension(MultiplexProtocolConnectionManager.NAME); diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyClientToServerTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyClientToServerTest.java index 79fe9b2571..2f0f6e03fb 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyClientToServerTest.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyClientToServerTest.java @@ -17,12 +17,19 @@ package org.apache.dubbo.remoting.transport.netty4; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeServer; import org.apache.dubbo.remoting.exchange.Exchangers; import org.apache.dubbo.remoting.exchange.support.Replier; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ModuleModel; + +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; /** * Netty4ClientToServerTest @@ -32,7 +39,20 @@ class NettyClientToServerTest extends ClientToServerTest { protected ExchangeServer newServer(int port, Replier receiver) throws RemotingException { // add heartbeat cycle to avoid unstable ut. URL url = URL.valueOf("exchange://localhost:" + port + "?server=netty4"); - url = url.addParameter(Constants.HEARTBEAT_KEY, 600 * 1000); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + ConfigManager configManager = new ConfigManager(applicationModel); + configManager.setApplication(applicationConfig); + configManager.getApplication(); + applicationModel.setConfigManager(configManager); + url = url.addParameter(Constants.HEARTBEAT_KEY, 600 * 1000).putAttribute(CommonConstants.SCOPE_MODEL, applicationModel); + url = url.setScopeModel(applicationModel); +// ModuleModel moduleModel = applicationModel.getDefaultModule(); + + ModuleModel moduleModel = applicationModel.getDefaultModule(); + url = url.putAttribute(CommonConstants.SCOPE_MODEL, moduleModel); return Exchangers.bind(url, receiver); } @@ -40,6 +60,17 @@ class NettyClientToServerTest extends ClientToServerTest { // add heartbeat cycle to avoid unstable ut. URL url = URL.valueOf("exchange://localhost:" + port + "?client=netty4&timeout=3000"); url = url.addParameter(Constants.HEARTBEAT_KEY, 600 * 1000); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + ConfigManager configManager = new ConfigManager(applicationModel); + configManager.setApplication(applicationConfig); + configManager.getApplication(); + applicationModel.setConfigManager(configManager); + url = url.setScopeModel(applicationModel); + ModuleModel moduleModel = applicationModel.getDefaultModule(); + url = url.putAttribute(CommonConstants.SCOPE_MODEL, moduleModel); return Exchangers.connect(url); } diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyTransporterTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyTransporterTest.java index 17b0db79f2..772f3d604a 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyTransporterTest.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyTransporterTest.java @@ -17,18 +17,24 @@ package org.apache.dubbo.remoting.transport.netty4; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.RemotingServer; import org.apache.dubbo.remoting.transport.ChannelHandlerAdapter; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -39,6 +45,17 @@ class NettyTransporterTest { URL url = new ServiceConfigURL("telnet", "localhost", port, new String[]{Constants.BIND_PORT_KEY, String.valueOf(port)}); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + ConfigManager configManager = new ConfigManager(applicationModel); + configManager.setApplication(applicationConfig); + configManager.getApplication(); + applicationModel.setConfigManager(configManager); + url = url.setScopeModel(applicationModel); + ModuleModel moduleModel = applicationModel.getDefaultModule(); + url = url.putAttribute(CommonConstants.SCOPE_MODEL, moduleModel); RemotingServer server = new NettyTransporter().bind(url, new ChannelHandlerAdapter()); assertThat(server.isBound(), is(true)); @@ -51,7 +68,17 @@ class NettyTransporterTest { int port = NetUtils.getAvailablePort(); URL url = new ServiceConfigURL("telnet", "localhost", port, new String[]{Constants.BIND_PORT_KEY, String.valueOf(port)}); - + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + ConfigManager configManager = new ConfigManager(applicationModel); + configManager.setApplication(applicationConfig); + configManager.getApplication(); + applicationModel.setConfigManager(configManager); + url = url.setScopeModel(applicationModel); + ModuleModel moduleModel = applicationModel.getDefaultModule(); + url = url.putAttribute(CommonConstants.SCOPE_MODEL, moduleModel); new NettyTransporter().bind(url, new ChannelHandlerAdapter() { @Override diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationExchangerTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationExchangerTest.java index f06c018f3e..faa9de8869 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationExchangerTest.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationExchangerTest.java @@ -17,16 +17,22 @@ package org.apache.dubbo.remoting.transport.netty4; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient; import org.apache.dubbo.remoting.api.pu.DefaultPuHandler; import org.apache.dubbo.remoting.exchange.PortUnificationExchanger; - +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; + class PortUnificationExchangerTest { private static URL url; @@ -35,6 +41,17 @@ class PortUnificationExchangerTest { public static void init() throws RemotingException { int port = NetUtils.getAvailablePort(); url = URL.valueOf("empty://127.0.0.1:" + port + "?foo=bar"); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + ConfigManager configManager = new ConfigManager(applicationModel); + configManager.setApplication(applicationConfig); + configManager.getApplication(); + applicationModel.setConfigManager(configManager); + url = url.setScopeModel(applicationModel); + ModuleModel moduleModel = applicationModel.getDefaultModule(); + url = url.putAttribute(CommonConstants.SCOPE_MODEL, moduleModel); } @Test diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationServerTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationServerTest.java index 61273baf4c..0f5268da89 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationServerTest.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationServerTest.java @@ -17,20 +17,37 @@ package org.apache.dubbo.remoting.transport.netty4; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.api.pu.DefaultPuHandler; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; + class PortUnificationServerTest { @Test void testBind() throws RemotingException { int port = NetUtils.getAvailablePort(); URL url = URL.valueOf("empty://127.0.0.1:" + port + "?foo=bar"); - + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + ConfigManager configManager = new ConfigManager(applicationModel); + configManager.setApplication(applicationConfig); + configManager.getApplication(); + applicationModel.setConfigManager(configManager); + url = url.setScopeModel(applicationModel); + ModuleModel moduleModel = applicationModel.getDefaultModule(); + url = url.putAttribute(CommonConstants.SCOPE_MODEL, moduleModel); // abstract endpoint need to get codec of url(which is in triple package) final NettyPortUnificationServer server = new NettyPortUnificationServer(url, new DefaultPuHandler()); server.bind(); diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ReplierDispatcherTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ReplierDispatcherTest.java index 5d76aa0099..bc3f5d8cbb 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ReplierDispatcherTest.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ReplierDispatcherTest.java @@ -19,12 +19,15 @@ package org.apache.dubbo.remoting.transport.netty4; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeServer; import org.apache.dubbo.remoting.exchange.Exchangers; import org.apache.dubbo.remoting.exchange.support.ReplierDispatcher; - +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -38,6 +41,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; import static org.junit.jupiter.api.Assertions.fail; @@ -59,7 +63,19 @@ class ReplierDispatcherTest { ReplierDispatcher dispatcher = new ReplierDispatcher(); dispatcher.addReplier(RpcMessage.class, new RpcMessageHandler()); dispatcher.addReplier(Data.class, (channel, msg) -> new StringMessage("hello world")); - exchangeServer = Exchangers.bind(URL.valueOf("exchange://localhost:" + port + "?" + CommonConstants.TIMEOUT_KEY + "=60000"), dispatcher); + URL url = URL.valueOf("exchange://localhost:" + port + "?" + CommonConstants.TIMEOUT_KEY + "=60000"); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + ConfigManager configManager = new ConfigManager(applicationModel); + configManager.setApplication(applicationConfig); + configManager.getApplication(); + applicationModel.setConfigManager(configManager); + url = url.setScopeModel(applicationModel); + ModuleModel moduleModel = applicationModel.getDefaultModule(); + url = url.putAttribute(CommonConstants.SCOPE_MODEL, moduleModel); + exchangeServer = Exchangers.bind(url, dispatcher); } diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/MultiplexProtocolConnectionManagerTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/MultiplexProtocolConnectionManagerTest.java index 634c4a72c1..2bceec7ab6 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/MultiplexProtocolConnectionManagerTest.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/MultiplexProtocolConnectionManagerTest.java @@ -18,6 +18,9 @@ package org.apache.dubbo.remoting.transport.netty4.api; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient; import org.apache.dubbo.remoting.api.connection.ConnectionManager; @@ -25,6 +28,8 @@ import org.apache.dubbo.remoting.api.connection.MultiplexProtocolConnectionManag import org.apache.dubbo.remoting.api.pu.DefaultPuHandler; import org.apache.dubbo.remoting.transport.netty4.NettyPortUnificationServer; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -34,6 +39,8 @@ import java.lang.reflect.Field; import java.util.Map; import java.util.function.Consumer; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; + public class MultiplexProtocolConnectionManagerTest { private static URL url1; @@ -46,8 +53,21 @@ public class MultiplexProtocolConnectionManagerTest { @BeforeAll public static void init() throws RemotingException { + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + ConfigManager configManager = new ConfigManager(applicationModel); + configManager.setApplication(applicationConfig); + configManager.getApplication(); + applicationModel.setConfigManager(configManager); url1 = URL.valueOf("empty://127.0.0.1:8080?foo=bar"); url2 = URL.valueOf("tri://127.0.0.1:8081?foo=bar"); + url1 = url1.setScopeModel(applicationModel); + ModuleModel moduleModel = applicationModel.getDefaultModule(); + url1 = url1.putAttribute(CommonConstants.SCOPE_MODEL, moduleModel); + url2 = url2.setScopeModel(applicationModel); + url2 = url2.putAttribute(CommonConstants.SCOPE_MODEL, moduleModel); server = new NettyPortUnificationServer(url1, new DefaultPuHandler()); server.bind(); connectionManager = url1.getOrDefaultFrameworkModel() diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/SingleProtocolConnectionManagerTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/SingleProtocolConnectionManagerTest.java index fab70fd76e..bc4d548008 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/SingleProtocolConnectionManagerTest.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/SingleProtocolConnectionManagerTest.java @@ -18,7 +18,10 @@ package org.apache.dubbo.remoting.transport.netty4.api; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient; import org.apache.dubbo.remoting.api.connection.ConnectionManager; @@ -26,7 +29,8 @@ import org.apache.dubbo.remoting.api.connection.SingleProtocolConnectionManager; import org.apache.dubbo.remoting.api.pu.DefaultPuHandler; import org.apache.dubbo.remoting.transport.netty4.NettyConnectionClient; import org.apache.dubbo.remoting.transport.netty4.NettyPortUnificationServer; - +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -36,6 +40,8 @@ import java.lang.reflect.Field; import java.util.Map; import java.util.function.Consumer; +import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; + public class SingleProtocolConnectionManagerTest { private static URL url; @@ -48,6 +54,17 @@ public class SingleProtocolConnectionManagerTest { public static void init() throws RemotingException { int port = NetUtils.getAvailablePort(); url = URL.valueOf("empty://127.0.0.1:" + port + "?foo=bar"); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); + applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + ConfigManager configManager = new ConfigManager(applicationModel); + configManager.setApplication(applicationConfig); + configManager.getApplication(); + applicationModel.setConfigManager(configManager); + url = url.setScopeModel(applicationModel); + ModuleModel moduleModel = applicationModel.getDefaultModule(); + url = url.putAttribute(CommonConstants.SCOPE_MODEL, moduleModel); server = new NettyPortUnificationServer(url, new DefaultPuHandler()); server.bind(); connectionManager = url.getOrDefaultFrameworkModel() diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/resources/security/serialize.allowlist b/dubbo-remoting/dubbo-remoting-netty4/src/test/resources/security/serialize.allowlist new file mode 100644 index 0000000000..74b8dcef99 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/resources/security/serialize.allowlist @@ -0,0 +1,20 @@ +# +# +# 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. +# +# + +org.apache.dubbo diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java index d0e59d8554..a3759cfc84 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java @@ -182,7 +182,13 @@ public class AsyncRpcResult implements Result { public Result get() throws InterruptedException, ExecutionException { if (executor != null && executor instanceof ThreadlessExecutor) { ThreadlessExecutor threadlessExecutor = (ThreadlessExecutor) executor; - threadlessExecutor.waitAndDrain(); + try { + while (!responseFuture.isDone()) { + threadlessExecutor.waitAndDrain(); + } + } finally { + threadlessExecutor.shutdown(); + } } return responseFuture.get(); } @@ -191,7 +197,13 @@ public class AsyncRpcResult implements Result { public Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (executor != null && executor instanceof ThreadlessExecutor) { ThreadlessExecutor threadlessExecutor = (ThreadlessExecutor) executor; - threadlessExecutor.waitAndDrain(); + try { + while (!responseFuture.isDone()) { + threadlessExecutor.waitAndDrain(); + } + } finally { + threadlessExecutor.shutdown(); + } } return responseFuture.get(timeout, unit); } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/HeaderFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/HeaderFilter.java index e82b697f3e..983c8c5d8b 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/HeaderFilter.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/HeaderFilter.java @@ -16,9 +16,10 @@ */ package org.apache.dubbo.rpc; +import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; -@SPI +@SPI(scope = ExtensionScope.FRAMEWORK) public interface HeaderFilter { RpcInvocation invoke(Invoker invoker, RpcInvocation invocation) throws RpcException; diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ExporterScopeModelInitializer.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcScopeModelInitializer.java similarity index 83% rename from dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ExporterScopeModelInitializer.java rename to dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcScopeModelInitializer.java index 87092be949..d246431565 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ExporterScopeModelInitializer.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcScopeModelInitializer.java @@ -14,19 +14,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.rpc.listener; +package org.apache.dubbo.rpc; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; +import org.apache.dubbo.rpc.listener.InjvmExporterListener; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; +import org.apache.dubbo.rpc.protocol.PermittedSerializationKeeper; -public class ExporterScopeModelInitializer implements ScopeModelInitializer { +public class RpcScopeModelInitializer implements ScopeModelInitializer { @Override public void initializeFrameworkModel(FrameworkModel frameworkModel) { ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); beanFactory.registerBean(InjvmExporterListener.class); + beanFactory.registerBean(PermittedSerializationKeeper.class); } @Override diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ExporterChangeListener.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ExporterChangeListener.java index 669b70eb0f..64815f6f69 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ExporterChangeListener.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ExporterChangeListener.java @@ -18,10 +18,22 @@ package org.apache.dubbo.rpc.listener; import org.apache.dubbo.rpc.Exporter; - +/** + * An interface for listening to changes in the export state of an Exporter object. + */ public interface ExporterChangeListener { + /** + * This method is called when an Exporter object is exported. + * + * @param exporter The Exporter object that has been exported. + */ void onExporterChangeExport(Exporter exporter); + /** + * This method is called when an Exporter object is unexported. + * + * @param exporter The Exporter object that has been unexported. + */ void onExporterChangeUnExport(Exporter exporter); } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/InjvmExporterListener.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/InjvmExporterListener.java index 8b22bb0f5b..f4dbd0b557 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/InjvmExporterListener.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/InjvmExporterListener.java @@ -25,12 +25,42 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; + +/** + * The InjvmExporterListener class is an implementation of the ExporterListenerAdapter abstract class, + *

+ * which is used to listen for changes to the InjvmExporter instances. + *

+ * It maintains two ConcurrentHashMaps, one to keep track of the ExporterChangeListeners registered for each service, + *

+ * and another to keep track of the currently exported services and their associated Exporter instances. + *

+ * It overrides the exported and unexported methods to add or remove the corresponding Exporter instances to/from + *

+ * the exporters ConcurrentHashMap, and to notify all registered ExporterChangeListeners of the change. + *

+ * It also provides methods to add or remove ExporterChangeListeners for a specific service, and to retrieve the + *

+ * currently exported Exporter instance for a given service. + */ public class InjvmExporterListener extends ExporterListenerAdapter { - + /* + * A ConcurrentHashMap to keep track of the ExporterChangeListeners registered for each service. + */ private final Map> exporterChangeListeners = new ConcurrentHashMap<>(); - + /* + * A ConcurrentHashMap to keep track of the currently exported services and their associated Exporter instances + */ private final Map> exporters = new ConcurrentHashMap<>(); + /** + * Overrides the exported method to add the given exporter to the exporters ConcurrentHashMap, + *

+ * and to notify all registered ExporterChangeListeners of the export event. + * + * @param exporter The Exporter instance that has been exported. + * @throws RpcException If there is an error during the export process. + */ @Override public void exported(Exporter exporter) throws RpcException { String serviceKey = exporter.getInvoker().getUrl().getServiceKey(); @@ -44,6 +74,15 @@ public class InjvmExporterListener extends ExporterListenerAdapter { super.exported(exporter); } + + /** + * Overrides the unexported method to remove the given exporter from the exporters ConcurrentHashMap, + *

+ * and to notify all registered ExporterChangeListeners of the unexport event. + * + * @param exporter The Exporter instance that has been unexported. + * @throws RpcException If there is an error during the unexport process. + */ @Override public void unexported(Exporter exporter) throws RpcException { String serviceKey = exporter.getInvoker().getUrl().getServiceKey(); @@ -58,6 +97,14 @@ public class InjvmExporterListener extends ExporterListenerAdapter { super.unexported(exporter); } + /** + * Adds an ExporterChangeListener for a specific service, and notifies the listener of the current Exporter instance + *

+ * if it exists. + * + * @param listener The ExporterChangeListener to add. + * @param serviceKey The service key for the service to listen for changes on. + */ public synchronized void addExporterChangeListener(ExporterChangeListener listener, String serviceKey) { exporterChangeListeners.putIfAbsent(serviceKey, new ConcurrentHashSet<>()); exporterChangeListeners.get(serviceKey).add(listener); @@ -67,8 +114,17 @@ public class InjvmExporterListener extends ExporterListenerAdapter { } } + /** + * Removes an ExporterChangeListener for a specific service. + * + * @param listener The ExporterChangeListener to remove. + * @param listenerKey The service key for the service to remove the listener from. + */ public synchronized void removeExporterChangeListener(ExporterChangeListener listener, String listenerKey) { Set listeners = exporterChangeListeners.get(listenerKey); + if (CollectionUtils.isEmpty(listeners)) { + return; + } listeners.remove(listener); if (CollectionUtils.isEmpty(listeners)) { exporterChangeListeners.remove(listenerKey); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProtocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProtocol.java index 3781b2f382..fd49e60298 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProtocol.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProtocol.java @@ -23,6 +23,7 @@ import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.support.SerializableClassRegistry; import org.apache.dubbo.common.serialize.support.SerializationOptimizer; import org.apache.dubbo.common.utils.ConcurrentHashSet; +import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.rpc.Exporter; @@ -43,9 +44,11 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_SERVER_SHUTDOWN_TIMEOUT; -import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.OPTIMIZER_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_DESTROY_INVOKER; /** @@ -181,4 +184,13 @@ public abstract class AbstractProtocol implements Protocol, ScopeModelAware { } } + + protected String getAddr(URL url) { + String bindIp = url.getParameter(org.apache.dubbo.remoting.Constants.BIND_IP_KEY, url.getHost()); + if (url.getParameter(ANYHOST_KEY, false)) { + bindIp = ANYHOST_VALUE; + } + return NetUtils.getIpByHost(bindIp) + ":" + url.getParameter(org.apache.dubbo.remoting.Constants.BIND_PORT_KEY, url.getPort()); + } + } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java index 360e629c72..db55424ed6 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java @@ -19,11 +19,9 @@ package org.apache.dubbo.rpc.protocol; import org.apache.dubbo.common.Parameters; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; -import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.RemotingServer; import org.apache.dubbo.rpc.Exporter; @@ -43,8 +41,6 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; -import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNSUPPORTED; /** @@ -146,14 +142,6 @@ public abstract class AbstractProxyProtocol extends AbstractProtocol { return re; } - protected String getAddr(URL url) { - String bindIp = url.getParameter(Constants.BIND_IP_KEY, url.getHost()); - if (url.getParameter(ANYHOST_KEY, false)) { - bindIp = ANYHOST_VALUE; - } - return NetUtils.getIpByHost(bindIp) + ":" + url.getParameter(Constants.BIND_PORT_KEY, url.getPort()); - } - protected int getErrorCode(Throwable e) { return RpcException.UNKNOWN_EXCEPTION; } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/PermittedSerializationKeeper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/PermittedSerializationKeeper.java new file mode 100644 index 0000000000..7bc8d12017 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/PermittedSerializationKeeper.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; +import org.apache.dubbo.common.utils.ConcurrentHashSet; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.remoting.transport.CodecSupport; +import org.apache.dubbo.remoting.utils.UrlUtils; + +import java.io.IOException; +import java.util.Collection; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import static org.apache.dubbo.common.BaseServiceMetadata.interfaceFromServiceKey; +import static org.apache.dubbo.common.BaseServiceMetadata.versionFromServiceKey; + +public class PermittedSerializationKeeper { + private final ConcurrentMap> serviceToSerializationId = new ConcurrentHashMap<>(); + private final Set globalPermittedSerializationIds = new ConcurrentHashSet<>(); + + public void registerService(URL url) { + Set set = ConcurrentHashMapUtils.computeIfAbsent(serviceToSerializationId, keyWithoutGroup(url.getServiceKey()), k -> new ConcurrentHashSet<>()); + Collection serializations = UrlUtils.allSerializations(url); + for (String serialization : serializations) { + Byte id = CodecSupport.getIDByName(serialization); + if (id != null) { + set.add(id); + globalPermittedSerializationIds.add(id); + } + } + } + + public boolean checkSerializationPermitted(String serviceKeyWithoutGroup, Byte id) throws IOException { + Set set = serviceToSerializationId.get(serviceKeyWithoutGroup); + if (set == null) { + throw new IOException("Service " + serviceKeyWithoutGroup + " not found, invocation rejected."); + } + return set.contains(id); + } + + private static String keyWithoutGroup(String serviceKey) { + String interfaceName = interfaceFromServiceKey(serviceKey); + String version = versionFromServiceKey(serviceKey); + if (StringUtils.isEmpty(version)) { + return interfaceName; + } + return interfaceName + CommonConstants.GROUP_CHAR_SEPARATOR + version; + } +} diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolSerializationWrapper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolSerializationWrapper.java index 6507d6a84e..dfaaffc827 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolSerializationWrapper.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolSerializationWrapper.java @@ -43,7 +43,7 @@ public class ProtocolSerializationWrapper implements Protocol { @Override public Exporter export(Invoker invoker) throws RpcException { - getFrameworkModel(invoker.getUrl().getScopeModel()).getServiceRepository().registerProviderUrl(invoker.getUrl()); + getFrameworkModel(invoker.getUrl().getScopeModel()).getBeanFactory().getBean(PermittedSerializationKeeper.class).registerService(invoker.getUrl()); return protocol.export(invoker); } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java index f8d6472bab..957e318fa7 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java @@ -248,7 +248,7 @@ public final class AccessLogData { Object[] args = get(ARGUMENTS) != null ? (Object[]) get(ARGUMENTS) : null; if (args != null && args.length > 0) { - sn.append(JsonUtils.getJson().toJson(args)); + sn.append(JsonUtils.toJson(args)); } return sn.toString(); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockInvoker.java index 6469ba4333..eab455ce46 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockInvoker.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockInvoker.java @@ -79,11 +79,11 @@ final public class MockInvoker implements Invoker { } else if (returnTypes != null && returnTypes.length > 0 && returnTypes[0] == String.class) { value = mock; } else if (StringUtils.isNumeric(mock, false)) { - value = JsonUtils.getJson().toJavaObject(mock, Object.class); + value = JsonUtils.toJavaObject(mock, Object.class); } else if (mock.startsWith("{")) { - value = JsonUtils.getJson().toJavaObject(mock, Map.class); + value = JsonUtils.toJavaObject(mock, Map.class); } else if (mock.startsWith("[")) { - value = JsonUtils.getJson().toJavaList(mock, Object.class); + value = JsonUtils.toJavaList(mock, Object.class); } else { value = mock; } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer index 4d3c435bef..3c1f139fa0 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer +++ b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer @@ -1 +1 @@ -injvm=org.apache.dubbo.rpc.listener.ExporterScopeModelInitializer +rpc=org.apache.dubbo.rpc.RpcScopeModelInitializer diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/resources/security/serialize.allowlist b/dubbo-rpc/dubbo-rpc-api/src/test/resources/security/serialize.allowlist new file mode 100644 index 0000000000..74b8dcef99 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/test/resources/security/serialize.allowlist @@ -0,0 +1,20 @@ +# +# +# 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. +# +# + +org.apache.dubbo diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ByteAccessor.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ByteAccessor.java new file mode 100644 index 0000000000..e0913486da --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ByteAccessor.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.rpc.protocol.dubbo; + +import org.apache.dubbo.common.extension.SPI; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.exchange.Request; + +import java.io.InputStream; + +import static org.apache.dubbo.common.extension.ExtensionScope.FRAMEWORK; + +/** + * Extension of Byte Accessor, holding attributes as RpcInvocation objects + * so that the decoded service can be used more flexibly + * @since 3.2.0 + */ +@SPI(scope = FRAMEWORK) +public interface ByteAccessor { + + /** + * Get an enhanced DecodeableRpcInvocation subclass to allow custom decode. + * The parameters are the same as {@link DecodeableRpcInvocation} + */ + DecodeableRpcInvocation getRpcInvocation(Channel channel, Request req, InputStream is, byte proto); +} diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ClientsProvider.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ClientsProvider.java new file mode 100644 index 0000000000..6e00ed6a0b --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ClientsProvider.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.dubbo; + +import org.apache.dubbo.remoting.exchange.ExchangeClient; + +import java.util.List; + +public interface ClientsProvider { + List getClients(); + + void close(int timeout); +} diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java index c133913f9b..3f2127c0b5 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java @@ -16,21 +16,22 @@ */ package org.apache.dubbo.rpc.protocol.dubbo; - -import org.apache.dubbo.common.utils.CacheableSupplier; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.Cleanable; import org.apache.dubbo.common.serialize.ObjectInput; import org.apache.dubbo.common.utils.Assert; +import org.apache.dubbo.common.utils.CacheableSupplier; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Codec; import org.apache.dubbo.remoting.Decodeable; +import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.transport.CodecSupport; +import org.apache.dubbo.remoting.transport.ExceedPayloadLimitException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; @@ -39,6 +40,7 @@ import org.apache.dubbo.rpc.model.MethodDescriptor; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; +import org.apache.dubbo.rpc.protocol.PermittedSerializationKeeper; import org.apache.dubbo.rpc.support.RpcUtils; import java.io.IOException; @@ -53,28 +55,32 @@ import static org.apache.dubbo.common.URL.buildKey; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.PAYLOAD; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_DECODE; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_EXCEED_PAYLOAD_LIMIT; import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY; import static org.apache.dubbo.rpc.Constants.SERIALIZATION_SECURITY_CHECK_KEY; public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Decodeable { - private static final ErrorTypeAwareLogger log = LoggerFactory.getErrorTypeAwareLogger(DecodeableRpcInvocation.class); + protected static final ErrorTypeAwareLogger log = LoggerFactory.getErrorTypeAwareLogger(DecodeableRpcInvocation.class); - private final Channel channel; + protected final transient Channel channel; - private final byte serializationType; + protected final byte serializationType; - private final InputStream inputStream; + protected final transient InputStream inputStream; - private final Request request; + protected final transient Request request; - private volatile boolean hasDecoded; + protected volatile boolean hasDecoded; protected final FrameworkModel frameworkModel; - private final Supplier callbackServiceCodecFactory; + protected final transient Supplier callbackServiceCodecFactory; + + private static final boolean CHECK_SERIALIZATION = Boolean.parseBoolean(System.getProperty(SERIALIZATION_SECURITY_CHECK_KEY, "true")); public DecodeableRpcInvocation(FrameworkModel frameworkModel, Channel channel, Request request, InputStream is, byte id) { this.frameworkModel = frameworkModel; @@ -85,7 +91,7 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec this.request = request; this.inputStream = is; this.serializationType = id; - this.callbackServiceCodecFactory = CacheableSupplier.newSupplier(()-> + this.callbackServiceCodecFactory = CacheableSupplier.newSupplier(() -> new CallbackServiceCodec(frameworkModel)); } @@ -111,10 +117,6 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec throw new UnsupportedOperationException(); } - private void checkSerializationTypeFromRemote() { - - } - @Override public Object decode(Channel channel, InputStream input) throws IOException { ObjectInput in = CodecSupport.getSerialization(serializationType) @@ -130,6 +132,10 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec String version = in.readUTF(); setAttachment(VERSION_KEY, version); + // Do provider-level payload checks. + String keyWithoutGroup = keyWithoutGroup(path, version); + checkPayload(keyWithoutGroup); + setMethodName(in.readUTF()); String desc = in.readUTF(); @@ -137,84 +143,23 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec ClassLoader originClassLoader = Thread.currentThread().getContextClassLoader(); try { - if (Boolean.parseBoolean(System.getProperty(SERIALIZATION_SECURITY_CHECK_KEY, "true"))) { - CodecSupport.checkSerialization(frameworkModel.getServiceRepository(), path, version, serializationType); + if (CHECK_SERIALIZATION) { + PermittedSerializationKeeper keeper = frameworkModel.getBeanFactory().getBean(PermittedSerializationKeeper.class); + if (!keeper.checkSerializationPermitted(keyWithoutGroup, serializationType)) { + throw new IOException("Unexpected serialization id:" + serializationType + " received from network, please check if the peer send the right id."); + } } Object[] args = DubboCodec.EMPTY_OBJECT_ARRAY; Class[] pts = DubboCodec.EMPTY_CLASS_ARRAY; if (desc.length() > 0) { -// if (RpcUtils.isGenericCall(path, getMethodName()) || RpcUtils.isEcho(path, getMethodName())) { -// pts = ReflectUtils.desc2classArray(desc); -// } else { - FrameworkServiceRepository repository = frameworkModel.getServiceRepository(); - List providerModels = repository.lookupExportedServicesWithoutGroup(keyWithoutGroup(path, version)); - ServiceDescriptor serviceDescriptor = null; - if (CollectionUtils.isNotEmpty(providerModels)) { - for (ProviderModel providerModel : providerModels) { - serviceDescriptor = providerModel.getServiceModel(); - if (serviceDescriptor != null) { - break; - } - } - } - if (serviceDescriptor == null) { - // Unable to find ProviderModel from Exported Services - for (ApplicationModel applicationModel : frameworkModel.getApplicationModels()) { - for (ModuleModel moduleModel : applicationModel.getModuleModels()) { - serviceDescriptor = moduleModel.getServiceRepository().lookupService(path); - if (serviceDescriptor != null) { - break; - } - } - } - } - - if (serviceDescriptor != null) { - MethodDescriptor methodDescriptor = serviceDescriptor.getMethod(getMethodName(), desc); - if (methodDescriptor != null) { - pts = methodDescriptor.getParameterClasses(); - this.setReturnTypes(methodDescriptor.getReturnTypes()); - - // switch TCCL - if (CollectionUtils.isNotEmpty(providerModels)) { - if (providerModels.size() == 1) { - Thread.currentThread().setContextClassLoader(providerModels.get(0).getClassLoader()); - } else { - // try all providerModels' classLoader can load pts, use the first one - for (ProviderModel providerModel : providerModels) { - ClassLoader classLoader = providerModel.getClassLoader(); - boolean match = true; - for (Class pt : pts) { - try { - if (!pt.equals(classLoader.loadClass(pt.getName()))) { - match = false; - } - } catch (ClassNotFoundException e) { - match = false; - } - } - if (match) { - Thread.currentThread().setContextClassLoader(classLoader); - break; - } - } - } - } - } - } - + pts = drawPts(path, version, desc, pts); if (pts == DubboCodec.EMPTY_CLASS_ARRAY) { if (!RpcUtils.isGenericCall(desc, getMethodName()) && !RpcUtils.isEcho(desc, getMethodName())) { throw new IllegalArgumentException("Service not found:" + path + ", " + getMethodName()); } pts = ReflectUtils.desc2classArray(desc); } -// } - - args = new Object[pts.length]; - for (int i = 0; i < args.length; i++) { - args[i] = in.readObject(pts[i]); - } + args = drawArgs(in, pts); } setParameterTypes(pts); @@ -223,17 +168,7 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec addObjectAttachments(map); } - //decode argument ,may be callback - CallbackServiceCodec callbackServiceCodec = callbackServiceCodecFactory.get(); - for (int i = 0; i < args.length; i++) { - args[i] = callbackServiceCodec.decodeInvocationArgument(channel, this, pts, i, args[i]); - } - - setArguments(args); - String targetServiceName = buildKey(getAttachment(PATH_KEY), - getAttachment(GROUP_KEY), - getAttachment(VERSION_KEY)); - setTargetServiceUniqueName(targetServiceName); + decodeArgument(channel, pts, args); } catch (ClassNotFoundException e) { throw new IOException(StringUtils.toString("Read invocation data failed.", e)); } finally { @@ -245,4 +180,110 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec return this; } + + protected void decodeArgument(Channel channel, Class[] pts, Object[] args) throws IOException { + CallbackServiceCodec callbackServiceCodec = callbackServiceCodecFactory.get(); + for (int i = 0; i < args.length; i++) { + args[i] = callbackServiceCodec.decodeInvocationArgument(channel, this, pts, i, args[i]); + } + + setArguments(args); + String targetServiceName = buildKey(getAttachment(PATH_KEY), + getAttachment(GROUP_KEY), + getAttachment(VERSION_KEY)); + setTargetServiceUniqueName(targetServiceName); + } + + protected Class[] drawPts(String path, String version, String desc, Class[] pts) { + FrameworkServiceRepository repository = frameworkModel.getServiceRepository(); + List providerModels = repository.lookupExportedServicesWithoutGroup(keyWithoutGroup(path, version)); + ServiceDescriptor serviceDescriptor = null; + if (CollectionUtils.isNotEmpty(providerModels)) { + for (ProviderModel providerModel : providerModels) { + serviceDescriptor = providerModel.getServiceModel(); + if (serviceDescriptor != null) { + break; + } + } + } + if (serviceDescriptor == null) { + // Unable to find ProviderModel from Exported Services + for (ApplicationModel applicationModel : frameworkModel.getApplicationModels()) { + for (ModuleModel moduleModel : applicationModel.getModuleModels()) { + serviceDescriptor = moduleModel.getServiceRepository().lookupService(path); + if (serviceDescriptor != null) { + break; + } + } + } + } + + if (serviceDescriptor != null) { + MethodDescriptor methodDescriptor = serviceDescriptor.getMethod(getMethodName(), desc); + if (methodDescriptor != null) { + pts = methodDescriptor.getParameterClasses(); + this.setReturnTypes(methodDescriptor.getReturnTypes()); + + // switch TCCL + if (CollectionUtils.isNotEmpty(providerModels)) { + if (providerModels.size() == 1) { + Thread.currentThread().setContextClassLoader(providerModels.get(0).getClassLoader()); + } else { + // try all providerModels' classLoader can load pts, use the first one + for (ProviderModel providerModel : providerModels) { + ClassLoader classLoader = providerModel.getClassLoader(); + boolean match = true; + for (Class pt : pts) { + try { + if (!pt.equals(classLoader.loadClass(pt.getName()))) { + match = false; + } + } catch (ClassNotFoundException e) { + match = false; + } + } + if (match) { + Thread.currentThread().setContextClassLoader(classLoader); + break; + } + } + } + } + } + } + return pts; + } + + protected Object[] drawArgs(ObjectInput in, Class[] pts) throws IOException, ClassNotFoundException { + Object[] args; + args = new Object[pts.length]; + for (int i = 0; i < args.length; i++) { + args[i] = in.readObject(pts[i]); + } + return args; + } + + private void checkPayload(String serviceKey) throws IOException { + ProviderModel providerModel = + frameworkModel.getServiceRepository().lookupExportedServiceWithoutGroup(serviceKey); + if (providerModel != null) { + String payloadStr = (String) providerModel.getServiceMetadata().getAttachments().get(PAYLOAD); + if (payloadStr != null) { + int payload = Integer.parseInt(payloadStr); + if (payload <= 0) { + return; + } + if (request.getPayload() > payload) { + ExceedPayloadLimitException e = new ExceedPayloadLimitException( + "Data length too large: " + request.getPayload() + ", max payload: " + payload + ", channel: " + channel); + log.error(TRANSPORT_EXCEED_PAYLOAD_LIMIT, "", "", e.getMessage(), e); + throw e; + } + } + } + } + + protected void fillInvoker(DubboProtocol dubboProtocol) throws RemotingException { + this.setInvoker(dubboProtocol.getInvoker(channel, this)); + } } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java index 88a0f5622d..9f3a3351af 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java @@ -42,15 +42,16 @@ import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; +import static org.apache.dubbo.common.constants.CommonConstants.BYTE_ACCESSOR_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_ISOLATION; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_DECODE; -import static org.apache.dubbo.rpc.protocol.dubbo.Constants.DECODE_IN_IO_THREAD_KEY; import static org.apache.dubbo.rpc.protocol.dubbo.Constants.DEFAULT_DECODE_IN_IO_THREAD; /** @@ -73,10 +74,16 @@ public class DubboCodec extends ExchangeCodec { private static final AtomicBoolean decodeInUserThreadLogged = new AtomicBoolean(false); private final CallbackServiceCodec callbackServiceCodec; private final FrameworkModel frameworkModel; + private final ByteAccessor customByteAccessor; + private static final String DECODE_IN_IO_THREAD_KEY = "decode.in.io.thread"; public DubboCodec(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; callbackServiceCodec = new CallbackServiceCodec(frameworkModel); + customByteAccessor = Optional.ofNullable(System.getProperty(BYTE_ACCESSOR_KEY)) + .filter(StringUtils::isNotBlank) + .map(key -> frameworkModel.getExtensionLoader(ByteAccessor.class).getExtension(key)) + .orElse(null); } @Override @@ -109,12 +116,12 @@ public class DubboCodec extends ExchangeCodec { DecodeableRpcResult result; if (channel.getUrl().getParameter(DECODE_IN_IO_THREAD_KEY, DEFAULT_DECODE_IN_IO_THREAD)) { result = new DecodeableRpcResult(channel, res, is, - (Invocation) getRequestData(id), proto); + (Invocation) getRequestData(channel, res, id), proto); result.decode(); } else { result = new DecodeableRpcResult(channel, res, new UnsafeByteArrayInputStream(readMessageData(is)), - (Invocation) getRequestData(id), proto); + (Invocation) getRequestData(channel, res, id), proto); } data = result; } @@ -150,14 +157,28 @@ public class DubboCodec extends ExchangeCodec { } req.setEvent(true); } else { - req = new HeartBeatRequest(id); + req = new Request(id); + + // get data length. + int len = Bytes.bytes2int(header, 12); + req.setPayload(len); + DecodeableRpcInvocation inv; if (isDecodeDataInIoThread(channel)) { - inv = new DecodeableRpcInvocation(frameworkModel, channel, req, is, proto); + if (customByteAccessor != null) { + inv = customByteAccessor.getRpcInvocation(channel, req, new UnsafeByteArrayInputStream(readMessageData(is)), proto); + } else { + inv = new DecodeableRpcInvocation(frameworkModel, channel, req, new UnsafeByteArrayInputStream(readMessageData(is)), proto); + } inv.decode(); } else { - inv = new DecodeableRpcInvocation(frameworkModel, channel, req, - new UnsafeByteArrayInputStream(readMessageData(is)), proto); + if (customByteAccessor != null) { + inv = customByteAccessor.getRpcInvocation(channel, req, + new UnsafeByteArrayInputStream(readMessageData(is)), proto); + } else { + inv = new DecodeableRpcInvocation(frameworkModel, channel, req, + new UnsafeByteArrayInputStream(readMessageData(is)), proto); + } } data = inv; } @@ -179,17 +200,29 @@ public class DubboCodec extends ExchangeCodec { } private boolean isDecodeDataInIoThread(Channel channel) { - boolean decodeDataInIoThread = channel.getUrl().getParameter(DECODE_IN_IO_THREAD_KEY, DEFAULT_DECODE_IN_IO_THREAD); + Object obj = channel.getAttribute(DECODE_IN_IO_THREAD_KEY); + if (obj instanceof Boolean) { + return (Boolean) obj; + } + String mode = ExecutorRepository.getMode(channel.getUrl().getOrDefaultApplicationModel()); - if (EXECUTOR_MANAGEMENT_MODE_ISOLATION.equals(mode)) { - if (!decodeDataInIoThread && decodeInUserThreadLogged.compareAndSet(false, true)) { - log.info("Because thread pool isolation is enabled on the dubbo protocol, the body can only be decoded " + - "on the io thread, and the parameter[" + DECODE_IN_IO_THREAD_KEY + "] will be ignored"); - // Why? because obtaining the isolated thread pool requires the serviceKey of the service, - // and this part must be decoded before it can be obtained (more see DubboExecutorSupport) - } + boolean isIsolated = EXECUTOR_MANAGEMENT_MODE_ISOLATION.equals(mode); + + if (isIsolated && !decodeInUserThreadLogged.compareAndSet(false, true)) { + channel.setAttribute(DECODE_IN_IO_THREAD_KEY, true); return true; } + + boolean decodeDataInIoThread = channel.getUrl().getParameter(DECODE_IN_IO_THREAD_KEY, DEFAULT_DECODE_IN_IO_THREAD); + if (isIsolated && !decodeDataInIoThread) { + log.info("Because thread pool isolation is enabled on the dubbo protocol, the body can only be decoded " + + "on the io thread, and the parameter[" + DECODE_IN_IO_THREAD_KEY + "] will be ignored"); + // Why? because obtaining the isolated thread pool requires the serviceKey of the service, + // and this part must be decoded before it can be obtained (more see DubboExecutorSupport) + channel.setAttribute(DECODE_IN_IO_THREAD_KEY, true); + return true; + } + channel.setAttribute(DECODE_IN_IO_THREAD_KEY, decodeDataInIoThread); return decodeDataInIoThread; } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java index c5cbd1b23d..239f88d32d 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java @@ -37,6 +37,7 @@ import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.protocol.AbstractInvoker; import org.apache.dubbo.rpc.support.RpcUtils; +import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; @@ -46,10 +47,9 @@ import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.PAYLOAD; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.PAYLOAD; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_CLIENT; import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; /** @@ -57,7 +57,7 @@ import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; */ public class DubboInvoker extends AbstractInvoker { - private final ExchangeClient[] clients; + private final ClientsProvider clientsProvider; private final AtomicPositiveInteger index = new AtomicPositiveInteger(); @@ -68,13 +68,13 @@ public class DubboInvoker extends AbstractInvoker { private final int serverShutdownTimeout; - public DubboInvoker(Class serviceType, URL url, ExchangeClient[] clients) { - this(serviceType, url, clients, null); + public DubboInvoker(Class serviceType, URL url, ClientsProvider clientsProvider) { + this(serviceType, url, clientsProvider, null); } - public DubboInvoker(Class serviceType, URL url, ExchangeClient[] clients, Set> invokers) { + public DubboInvoker(Class serviceType, URL url, ClientsProvider clientsProvider, Set> invokers) { super(serviceType, url, new String[]{INTERFACE_KEY, GROUP_KEY, TOKEN_KEY}); - this.clients = clients; + this.clientsProvider = clientsProvider; this.invokers = invokers; this.serverShutdownTimeout = ConfigurationUtils.getServerShutdownTimeout(getUrl().getScopeModel()); } @@ -87,10 +87,11 @@ public class DubboInvoker extends AbstractInvoker { inv.setAttachment(VERSION_KEY, version); ExchangeClient currentClient; - if (clients.length == 1) { - currentClient = clients[0]; + List exchangeClients = clientsProvider.getClients(); + if (exchangeClients.size() == 1) { + currentClient = exchangeClients.get(0); } else { - currentClient = clients[index.getAndIncrement() % clients.length]; + currentClient = exchangeClients.get(index.getAndIncrement() % exchangeClients.size()); } try { boolean isOneway = RpcUtils.isOneway(getUrl(), invocation); @@ -143,7 +144,7 @@ public class DubboInvoker extends AbstractInvoker { if (!super.isAvailable()) { return false; } - for (ExchangeClient client : clients) { + for (ExchangeClient client : clientsProvider.getClients()) { if (client.isConnected() && !client.hasAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY)) { //cannot write == not Available ? return true; @@ -168,14 +169,7 @@ public class DubboInvoker extends AbstractInvoker { if (invokers != null) { invokers.remove(this); } - for (ExchangeClient client : clients) { - try { - client.close(serverShutdownTimeout); - } catch (Throwable t) { - logger.warn(PROTOCOL_ERROR_CLOSE_CLIENT, "", "", t.getMessage(), t); - } - } - + clientsProvider.close(serverShutdownTimeout); } finally { destroyLock.unlock(); } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboIsolationExecutorSupport.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboIsolationExecutorSupport.java index 2beb1e403c..231aa546ca 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboIsolationExecutorSupport.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboIsolationExecutorSupport.java @@ -16,42 +16,57 @@ */ package org.apache.dubbo.rpc.protocol.dubbo; -import org.apache.dubbo.common.ServiceKey; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.executor.AbstractIsolationExecutorSupport; - -import java.util.Map; - -import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; +import org.apache.dubbo.rpc.model.FrameworkServiceRepository; +import org.apache.dubbo.rpc.model.ProviderModel; +import org.apache.dubbo.rpc.model.ServiceModel; public class DubboIsolationExecutorSupport extends AbstractIsolationExecutorSupport { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboIsolationExecutorSupport.class); + private final FrameworkServiceRepository frameworkServiceRepository; + private final DubboProtocol dubboProtocol; + public DubboIsolationExecutorSupport(URL url) { super(url); + frameworkServiceRepository = url.getOrDefaultFrameworkModel().getServiceRepository(); + dubboProtocol = DubboProtocol.getDubboProtocol(url.getOrDefaultFrameworkModel()); } @Override - protected ServiceKey getServiceKey(Object data) { + protected ProviderModel getProviderModel(Object data) { if (!(data instanceof Request)) { return null; } Request request = (Request) data; - if (!(request.getData() instanceof Invocation)) { + if (!(request.getData() instanceof DecodeableRpcInvocation)) { return null; } - Invocation inv = (Invocation) request.getData(); - Map attachments = inv.getAttachments(); - String interfaceName = attachments.get(PATH_KEY); - String version = attachments.get(VERSION_KEY); - String group = attachments.get(GROUP_KEY); - return new ServiceKey(interfaceName, version, group); + + try { + ((DecodeableRpcInvocation) request.getData()).fillInvoker(dubboProtocol); + } catch (RemotingException e) { + // ignore here, and this exception will being rethrow in DubboProtocol + } + + ServiceModel serviceModel = ((Invocation) request.getData()).getServiceModel(); + if (serviceModel instanceof ProviderModel) { + return (ProviderModel) serviceModel; + } + + String targetServiceUniqueName = ((Invocation) request.getData()).getTargetServiceUniqueName(); + if (StringUtils.isNotEmpty(targetServiceUniqueName)) { + return frameworkServiceRepository.lookupExportedService(targetServiceUniqueName); + } + + return null; } } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java index eb17a4f1e4..0e10031e4a 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java @@ -19,8 +19,8 @@ package org.apache.dubbo.rpc.protocol.dubbo; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.config.ConfigurationUtils; +import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.url.component.ServiceConfigURL; -import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; @@ -49,16 +49,16 @@ import org.apache.dubbo.rpc.protocol.AbstractProtocol; import java.net.InetSocketAddress; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; @@ -66,7 +66,6 @@ import static org.apache.dubbo.common.constants.CommonConstants.LAZY_CONNECT_KEY import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.STUB_EVENT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_CLIENT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_SERVER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_REFER_INVOKER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNSUPPORTED; @@ -105,9 +104,7 @@ public class DubboProtocol extends AbstractProtocol { * * Map */ - private final Map referenceClientMap = new ConcurrentHashMap<>(); - - private static final Object PENDING_OBJECT = new Object(); + private final Map referenceClientMap = new ConcurrentHashMap<>(); private final AtomicBoolean destroyed = new AtomicBoolean(); @@ -126,8 +123,7 @@ public class DubboProtocol extends AbstractProtocol { } Invocation inv = (Invocation) message; - Invoker invoker = getInvoker(channel, inv); - inv.setServiceModel(invoker.getUrl().getServiceModel()); + Invoker invoker = inv.getInvoker() == null ? getInvoker(channel, inv) : inv.getInvoker(); // switch TCCL if (invoker.getUrl().getServiceModel() != null) { Thread.currentThread().setContextClassLoader(invoker.getUrl().getServiceModel().getClassLoader()); @@ -238,6 +234,7 @@ public class DubboProtocol extends AbstractProtocol { return invocation; } }; + this.frameworkModel = frameworkModel; } /** @@ -293,7 +290,9 @@ public class DubboProtocol extends AbstractProtocol { ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress() + ", message:" + getInvocationWithoutData(inv)); } - return exporter.getInvoker(); + Invoker invoker = exporter.getInvoker(); + inv.setServiceModel(invoker.getUrl().getServiceModel()); + return invoker; } public Collection> getInvokers() { @@ -414,7 +413,7 @@ public class DubboProtocol extends AbstractProtocol { return invoker; } - private ExchangeClient[] getClients(URL url) { + private ClientsProvider getClients(URL url) { int connections = url.getParameter(CONNECTIONS_KEY, 0); // whether to share connection // if not configured, connection is shared, otherwise, one connection for one service @@ -427,17 +426,13 @@ public class DubboProtocol extends AbstractProtocol { : url.getParameter(SHARE_CONNECTIONS_KEY, (String) null); connections = Integer.parseInt(shareConnectionsStr); - List shareClients = getSharedClient(url, connections); - ExchangeClient[] clients = new ExchangeClient[connections]; - Arrays.setAll(clients, shareClients::get); - return clients; + return getSharedClient(url, connections); } - ExchangeClient[] clients = new ExchangeClient[connections]; - for (int i = 0; i < clients.length; i++) { - clients[i] = initClient(url); - } - return clients; + List clients = IntStream.range(0, connections) + .mapToObj((i) -> initClient(url)) + .collect(Collectors.toList()); + return new ExclusiveClientsProvider(clients); } /** @@ -447,106 +442,24 @@ public class DubboProtocol extends AbstractProtocol { * @param connectNum connectNum must be greater than or equal to 1 */ @SuppressWarnings("unchecked") - private List getSharedClient(URL url, int connectNum) { + private SharedClientsProvider getSharedClient(URL url, int connectNum) { String key = url.getAddress(); - Object clients = referenceClientMap.get(key); - if (clients instanceof List) { - List typedClients = (List) clients; - if (checkClientCanUse(typedClients)) { - batchClientRefIncr(typedClients); - return typedClients; - } - } - - List typedClients = null; - - synchronized (referenceClientMap) { - for (; ; ) { - // guarantee just one thread in loading condition. And Other is waiting It had finished. - clients = referenceClientMap.get(key); - - if (clients instanceof List) { - typedClients = (List) clients; - if (checkClientCanUse(typedClients)) { - batchClientRefIncr(typedClients); - return typedClients; - } else { - referenceClientMap.put(key, PENDING_OBJECT); - break; - } - } else if (clients == PENDING_OBJECT) { - try { - referenceClientMap.wait(); - } catch (InterruptedException ignored) { - } - } else { - referenceClientMap.put(key, PENDING_OBJECT); - break; - } - } - } - - try { - // connectNum must be greater than or equal to 1 - connectNum = Math.max(connectNum, 1); - - // If the clients is empty, then the first initialization is - if (CollectionUtils.isEmpty(typedClients)) { - typedClients = buildReferenceCountExchangeClientList(url, connectNum); + // connectNum must be greater than or equal to 1 + int expectedConnectNum = Math.max(connectNum, 1); + return referenceClientMap.compute(key, (originKey, originValue) -> { + if (originValue != null && originValue.increaseCount()) { + return originValue; } else { - for (int i = 0; i < typedClients.size(); i++) { - ReferenceCountExchangeClient referenceCountExchangeClient = typedClients.get(i); - // If there is a client in the list that is no longer available, create a new one to replace him. - if (referenceCountExchangeClient == null || referenceCountExchangeClient.isClosed()) { - typedClients.set(i, buildReferenceCountExchangeClient(url)); - continue; - } - referenceCountExchangeClient.incrementAndGetCount(); - } + return new SharedClientsProvider(this, originKey, buildReferenceCountExchangeClientList(url, expectedConnectNum)); } - } finally { - synchronized (referenceClientMap) { - if (typedClients == null) { - referenceClientMap.remove(key); - } else { - referenceClientMap.put(key, typedClients); - } - referenceClientMap.notifyAll(); - } - } - return typedClients; + }); } - /** - * Check if the client list is all available - * - * @param referenceCountExchangeClients - * @return true-available,false-unavailable - */ - private boolean checkClientCanUse(List referenceCountExchangeClients) { - if (CollectionUtils.isEmpty(referenceCountExchangeClients)) { - return false; - } - - // As long as one client is not available, you need to replace the unavailable client with the available one. - return referenceCountExchangeClients.stream() - .noneMatch(referenceCountExchangeClient -> referenceCountExchangeClient == null - || referenceCountExchangeClient.getCount() <= 0 || referenceCountExchangeClient.isClosed()); - } - - /** - * Increase the reference Count if we create new invoker shares same connection, the connection will be closed without any reference. - * - * @param referenceCountExchangeClients - */ - private void batchClientRefIncr(List referenceCountExchangeClients) { - if (CollectionUtils.isEmpty(referenceCountExchangeClients)) { - return; - } - referenceCountExchangeClients.stream() - .filter(Objects::nonNull) - .forEach(ReferenceCountExchangeClient::incrementAndGetCount); + protected void scheduleRemoveSharedClient(String key, SharedClientsProvider sharedClient) { + this.frameworkModel.getBeanFactory().getBean(FrameworkExecutorRepository.class) + .getSharedExecutor() + .submit(() -> referenceClientMap.remove(key, sharedClient)); } /** @@ -647,53 +560,16 @@ public class DubboProtocol extends AbstractProtocol { serverMap.clear(); for (String key : new ArrayList<>(referenceClientMap.keySet())) { - Object clients = referenceClientMap.remove(key); - if (clients instanceof List) { - List typedClients = (List) clients; - - if (CollectionUtils.isEmpty(typedClients)) { - continue; - } - - for (ReferenceCountExchangeClient client : typedClients) { - closeReferenceCountExchangeClient(client); - } - } + SharedClientsProvider clients = referenceClientMap.remove(key); + clients.forceClose(); } + PortUnificationExchanger.close(); referenceClientMap.clear(); super.destroy(); } - /** - * close ReferenceCountExchangeClient - * - * @param client - */ - private void closeReferenceCountExchangeClient(ReferenceCountExchangeClient client) { - if (client == null) { - return; - } - - try { - if (logger.isInfoEnabled()) { - logger.info("Close dubbo connect: " + client.getLocalAddress() + "-->" + client.getRemoteAddress()); - } - - client.close(client.getShutdownWaitTime()); - - // TODO - /* - * At this time, ReferenceCountExchangeClient#client has been replaced with LazyConnectExchangeClient. - * Do you need to call client.close again to ensure that LazyConnectExchangeClient is also closed? - */ - - } catch (Throwable t) { - logger.warn(PROTOCOL_ERROR_CLOSE_CLIENT, "", "", t.getMessage(), t); - } - } - /** * only log body in debugger mode for size & security consideration. * diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ExclusiveClientsProvider.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ExclusiveClientsProvider.java new file mode 100644 index 0000000000..b570421378 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ExclusiveClientsProvider.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.dubbo; + +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.remoting.exchange.ExchangeClient; + +import java.util.List; + +import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_CLIENT; + +public class ExclusiveClientsProvider implements ClientsProvider { + private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ExclusiveClientsProvider.class); + private final List clients; + + public ExclusiveClientsProvider(List clients) { + this.clients = clients; + } + + @Override + public List getClients() { + return clients; + } + + @Override + public void close(int timeout) { + for (ExchangeClient client : clients) { + try { + client.close(timeout); + } catch (Throwable t) { + logger.warn(PROTOCOL_ERROR_CLOSE_CLIENT, "", "", t.getMessage(), t); + } + } + } +} diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/SharedClientsProvider.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/SharedClientsProvider.java new file mode 100644 index 0000000000..1f527f7506 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/SharedClientsProvider.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.dubbo; + +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.CollectionUtils; + +import java.util.List; +import java.util.Objects; + +import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_CLIENT; + +public class SharedClientsProvider implements ClientsProvider { + private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SharedClientsProvider.class); + private final DubboProtocol dubboProtocol; + private final String addressKey; + private final List clients; + + public SharedClientsProvider(DubboProtocol dubboProtocol, String addressKey, List clients) { + this.dubboProtocol = dubboProtocol; + this.addressKey = addressKey; + this.clients = clients; + } + + @Override + public List getClients() { + return clients; + } + + public synchronized boolean increaseCount() { + if (checkClientCanUse(clients)) { + batchClientRefIncr(clients); + return true; + } + return false; + } + + @Override + public synchronized void close(int timeout) { + for (ReferenceCountExchangeClient client : clients) { + try { + client.close(timeout); + } catch (Throwable t) { + logger.warn(PROTOCOL_ERROR_CLOSE_CLIENT, "", "", t.getMessage(), t); + } + } + if (!checkClientCanUse(clients)) { + dubboProtocol.scheduleRemoveSharedClient(addressKey, this); + } + } + + public synchronized void forceClose() { + for (ReferenceCountExchangeClient client : clients) { + closeReferenceCountExchangeClient(client); + } + } + + /** + * Check if the client list is all available + * + * @param referenceCountExchangeClients + * @return true-available,false-unavailable + */ + private boolean checkClientCanUse(List referenceCountExchangeClients) { + if (CollectionUtils.isEmpty(referenceCountExchangeClients)) { + return false; + } + + // As long as one client is not available, you need to replace the unavailable client with the available one. + return referenceCountExchangeClients.stream() + .noneMatch(referenceCountExchangeClient -> referenceCountExchangeClient == null + || referenceCountExchangeClient.getCount() <= 0 || referenceCountExchangeClient.isClosed()); + } + + /** + * Increase the reference Count if we create new invoker shares same connection, the connection will be closed without any reference. + * + * @param referenceCountExchangeClients + */ + private void batchClientRefIncr(List referenceCountExchangeClients) { + if (CollectionUtils.isEmpty(referenceCountExchangeClients)) { + return; + } + referenceCountExchangeClients.stream() + .filter(Objects::nonNull) + .forEach(ReferenceCountExchangeClient::incrementAndGetCount); + } + + /** + * close ReferenceCountExchangeClient + * + * @param client + */ + private void closeReferenceCountExchangeClient(ReferenceCountExchangeClient client) { + if (client == null) { + return; + } + + try { + if (logger.isInfoEnabled()) { + logger.info("Close dubbo connect: " + client.getLocalAddress() + "-->" + client.getRemoteAddress()); + } + + client.close(client.getShutdownWaitTime()); + + // TODO + /* + * At this time, ReferenceCountExchangeClient#client has been replaced with LazyConnectExchangeClient. + * Do you need to call client.close again to ensure that LazyConnectExchangeClient is also closed? + */ + + } catch (Throwable t) { + logger.warn(PROTOCOL_ERROR_CLOSE_CLIENT, "", "", t.getMessage(), t); + } + } + +} diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java index 3bf847dd6d..badd7a6490 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java @@ -106,7 +106,7 @@ public class TraceFilter implements Filter { channel.send("\r\n" + RpcContext.getServiceContext().getRemoteAddress() + " -> " + invoker.getInterface().getName() + "." + invocation.getMethodName() - + "(" + JsonUtils.getJson().toJson(invocation.getArguments()) + ")" + " -> " + JsonUtils.getJson().toJson(result.getValue()) + + "(" + JsonUtils.toJson(invocation.getArguments()) + ")" + " -> " + JsonUtils.toJson(result.getValue()) + "\r\nelapsed: " + (end - start) + " ms." + "\r\n\r\n" + prompt); } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocationTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocationTest.java index da86b50045..f4a39cf5d0 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocationTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocationTest.java @@ -32,6 +32,7 @@ import org.apache.dubbo.remoting.transport.CodecSupport; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; +import org.apache.dubbo.rpc.protocol.PermittedSerializationKeeper; import org.apache.dubbo.rpc.protocol.dubbo.decode.MockChannel; import org.apache.dubbo.rpc.protocol.dubbo.support.DemoService; @@ -68,7 +69,8 @@ class DecodeableRpcInvocationTest { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); applicationModel.getDefaultModule().getServiceRepository().registerService(DemoService.class.getName(), DemoService.class); - frameworkModel.getServiceRepository().registerProviderUrl(url); + frameworkModel.getBeanFactory().getBean(PermittedSerializationKeeper.class) + .registerService(url); // Simulate the server to decode Channel channel = new MockChannel(); diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodecTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodecTest.java index 50593274d5..59e1f4c5a2 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodecTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodecTest.java @@ -22,6 +22,7 @@ import org.apache.dubbo.remoting.buffer.ChannelBuffer; import org.apache.dubbo.remoting.buffer.ChannelBuffers; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; +import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import org.apache.dubbo.remoting.exchange.support.MultiMessage; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.RpcInvocation; @@ -32,7 +33,9 @@ import org.apache.dubbo.rpc.protocol.dubbo.support.DemoService; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import static org.apache.dubbo.rpc.Constants.INPUT_KEY; import static org.apache.dubbo.rpc.Constants.OUTPUT_KEY; @@ -45,23 +48,25 @@ class DubboCountCodecTest { ChannelBuffer buffer = ChannelBuffers.buffer(2048); Channel channel = new MockChannel(); Assertions.assertEquals(Codec2.DecodeResult.NEED_MORE_INPUT, dubboCountCodec.decode(channel, buffer)); + List futures = new ArrayList<>(); for (int i = 0; i < 10; i++) { - Request request = new Request(1); + Request request = new Request(i); + futures.add(DefaultFuture.newFuture(channel, request, 1000, null)); RpcInvocation rpcInvocation = new RpcInvocation(null, "echo", DemoService.class.getName(), "", new Class[]{String.class}, new String[]{"yug"}); request.setData(rpcInvocation); dubboCountCodec.encode(channel, buffer, request); } for (int i = 0; i < 10; i++) { - Response response = new Response(1); + Response response = new Response(i); AppResponse appResponse = new AppResponse(i); response.setResult(appResponse); dubboCountCodec.encode(channel, buffer, response); } MultiMessage multiMessage = (MultiMessage) dubboCountCodec.decode(channel, buffer); - Assertions.assertEquals(multiMessage.size(), 20); + Assertions.assertEquals(20, multiMessage.size()); int requestCount = 0; int responseCount = 0; Iterator iterator = multiMessage.iterator(); @@ -79,6 +84,8 @@ class DubboCountCodecTest { } Assertions.assertEquals(requestCount, 10); Assertions.assertEquals(responseCount, 10); + + futures.forEach(DefaultFuture::cancel); } } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvailableTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvailableTest.java index 590bc0ad53..9766e3bba7 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvailableTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvailableTest.java @@ -36,6 +36,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; +import java.util.List; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; @@ -172,11 +173,12 @@ class DubboInvokerAvailableTest { } private ExchangeClient[] getClients(DubboInvoker invoker) throws Exception { - Field field = DubboInvoker.class.getDeclaredField("clients"); + Field field = DubboInvoker.class.getDeclaredField("clientsProvider"); field.setAccessible(true); - ExchangeClient[] clients = (ExchangeClient[]) field.get(invoker); - Assertions.assertEquals(1, clients.length); - return clients; + ClientsProvider clientsProvider = (ClientsProvider) field.get(invoker); + List clients = clientsProvider.getClients(); + Assertions.assertEquals(1, clients.size()); + return clients.toArray(new ExchangeClient[0]); } public class DemoServiceImpl implements IDemoService { diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java index f602aa6624..1c18774e56 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java @@ -205,7 +205,7 @@ class ReferenceCountExchangeClientTest { // invoker status is available because the default value of associated lazy client's initial state is true. Assertions.assertTrue(helloServiceInvoker.isAvailable(), "invoker status unavailable"); - // revive: initial the lazy client's exchange client again. + // revive: initial the lazy client's exchange client again. Assertions.assertEquals("hello", helloService.hello()); destroy(); @@ -285,11 +285,12 @@ class ReferenceCountExchangeClientTest { private List getInvokerClientList(Invoker invoker) { @SuppressWarnings("rawtypes") DubboInvoker dInvoker = (DubboInvoker) invoker; try { - Field clientField = DubboInvoker.class.getDeclaredField("clients"); + Field clientField = DubboInvoker.class.getDeclaredField("clientsProvider"); clientField.setAccessible(true); - ExchangeClient[] clients = (ExchangeClient[]) clientField.get(dInvoker); + ClientsProvider clientsProvider = (ClientsProvider) clientField.get(dInvoker); + List clients = clientsProvider.getClients(); - List clientList = new ArrayList(clients.length); + List clientList = new ArrayList(clients.size()); for (ExchangeClient client : clients) { clientList.add(client); } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/DubboTelnetDecodeTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/DubboTelnetDecodeTest.java index cecc45dad9..9f05c81fe2 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/DubboTelnetDecodeTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/DubboTelnetDecodeTest.java @@ -35,6 +35,7 @@ import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; +import org.apache.dubbo.rpc.protocol.PermittedSerializationKeeper; import org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation; import org.apache.dubbo.rpc.protocol.dubbo.DubboCodec; import org.apache.dubbo.rpc.protocol.dubbo.support.DemoService; @@ -53,8 +54,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static org.apache.dubbo.rpc.Constants.SERIALIZATION_SECURITY_CHECK_KEY; - /** * These junit tests aim to test unpack and stick pack of dubbo and telnet */ @@ -75,16 +74,11 @@ class DubboTelnetDecodeTest { public static void setup() { ModuleServiceRepository serviceRepository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository(); serviceRepository.registerService(DemoService.class); - - // disable org.apache.dubbo.remoting.transport.CodecSupport.checkSerialization to avoid error: - // java.io.IOException: Service org.apache.dubbo.rpc.protocol.dubbo.support.DemoService with version 0.0.0 not found, invocation rejected. - System.setProperty(SERIALIZATION_SECURITY_CHECK_KEY, "false"); } @AfterAll public static void teardown() { FrameworkModel.defaultModel().destroy(); - System.clearProperty(SERIALIZATION_SECURITY_CHECK_KEY); } @@ -481,6 +475,9 @@ class DubboTelnetDecodeTest { // register // frameworkModel.getServiceRepository().registerProviderUrl(); + FrameworkModel.defaultModel().getBeanFactory().getBean(PermittedSerializationKeeper.class) + .registerService(URL.valueOf("dubbo://127.0.0.1:20880/" + DemoService.class.getName() + "?version=0.0.0")); + return dubboByteBuf; } diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelHandlersTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/ChannelHandlersTest.java similarity index 93% rename from dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelHandlersTest.java rename to dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/ChannelHandlersTest.java index 8c7ccbf724..4876dd7b85 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelHandlersTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/ChannelHandlersTest.java @@ -14,13 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.remoting.transport.dispatcher; +package org.apache.dubbo.rpc.protocol.dubbo.managemode; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.transport.MultiMessageHandler; - +import org.apache.dubbo.remoting.transport.dispatcher.ChannelHandlers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/ConnectChannelHandlerTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/ConnectChannelHandlerTest.java similarity index 99% rename from dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/ConnectChannelHandlerTest.java rename to dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/ConnectChannelHandlerTest.java index c131b8bbe7..352a8c2738 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/ConnectChannelHandlerTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/ConnectChannelHandlerTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.remoting.handler; +package org.apache.dubbo.rpc.protocol.dubbo.managemode; import org.apache.dubbo.remoting.ExecutionException; import org.apache.dubbo.remoting.RemotingException; @@ -22,7 +22,6 @@ import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.transport.dispatcher.connection.ConnectionOrderedChannelHandler; import org.apache.dubbo.rpc.model.ApplicationModel; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -31,7 +30,6 @@ import org.junit.jupiter.api.Test; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicInteger; - class ConnectChannelHandlerTest extends WrappedChannelHandlerTest { @BeforeEach diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/MockedChannel.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/MockedChannel.java new file mode 100644 index 0000000000..baa35eac69 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/MockedChannel.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.dubbo.managemode; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.ChannelHandler; +import org.apache.dubbo.remoting.RemotingException; + +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Map; + +public class MockedChannel implements Channel { + private boolean isClosed; + private volatile boolean closing = false; + private URL url; + private ChannelHandler handler; + private Map map = new HashMap(); + + public MockedChannel() { + super(); + } + + + @Override + public URL getUrl() { + return url; + } + + @Override + public ChannelHandler getChannelHandler() { + + return this.handler; + } + + @Override + public InetSocketAddress getLocalAddress() { + + return null; + } + + @Override + public void send(Object message) throws RemotingException { + } + + @Override + public void send(Object message, boolean sent) throws RemotingException { + this.send(message); + } + + @Override + public void close() { + isClosed = true; + } + + @Override + public void close(int timeout) { + this.close(); + } + + @Override + public void startClose() { + closing = true; + } + + @Override + public boolean isClosed() { + return isClosed; + } + + @Override + public InetSocketAddress getRemoteAddress() { + return null; + } + + @Override + public boolean isConnected() { + return false; + } + + @Override + public boolean hasAttribute(String key) { + return map.containsKey(key); + } + + @Override + public Object getAttribute(String key) { + return map.get(key); + } + + @Override + public void setAttribute(String key, Object value) { + map.put(key, value); + } + + @Override + public void removeAttribute(String key) { + map.remove(key); + } +} diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/MockedChannelHandler.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/MockedChannelHandler.java new file mode 100644 index 0000000000..f2dc66abba --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/MockedChannelHandler.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.dubbo.managemode; + +import org.apache.dubbo.common.utils.ConcurrentHashSet; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.ChannelHandler; +import org.apache.dubbo.remoting.RemotingException; + +import java.util.Collections; +import java.util.Set; + +public class MockedChannelHandler implements ChannelHandler { + // ConcurrentMap channels = new ConcurrentHashMap(); + ConcurrentHashSet channels = new ConcurrentHashSet(); + + @Override + public void connected(Channel channel) throws RemotingException { + channels.add(channel); + } + + @Override + public void disconnected(Channel channel) throws RemotingException { + channels.remove(channel); + } + + @Override + public void sent(Channel channel, Object message) throws RemotingException { + channel.send(message); + } + + @Override + public void received(Channel channel, Object message) throws RemotingException { + //echo + channel.send(message); + } + + @Override + public void caught(Channel channel, Throwable exception) throws RemotingException { + throw new RemotingException(channel, exception); + + } + + public Set getChannels() { + return Collections.unmodifiableSet(channels); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/WrappedChannelHandlerTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/WrappedChannelHandlerTest.java similarity index 98% rename from dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/WrappedChannelHandlerTest.java rename to dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/WrappedChannelHandlerTest.java index 4ef2f5203a..e0e567d9b8 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/WrappedChannelHandlerTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/WrappedChannelHandlerTest.java @@ -14,8 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.remoting.handler; - +package org.apache.dubbo.rpc.protocol.dubbo.managemode; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; @@ -28,7 +27,6 @@ import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler; import org.apache.dubbo.rpc.model.ApplicationModel; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,7 +38,7 @@ import static org.junit.jupiter.api.Assertions.fail; class WrappedChannelHandlerTest { WrappedChannelHandler handler; - URL url = URL.valueOf("test://10.20.30.40:1234"); + URL url = URL.valueOf("dubbo://10.20.30.40:1234"); @BeforeEach public void setUp() throws Exception { diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/resources/security/serialize.allowlist b/dubbo-rpc/dubbo-rpc-dubbo/src/test/resources/security/serialize.allowlist new file mode 100644 index 0000000000..74b8dcef99 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/resources/security/serialize.allowlist @@ -0,0 +1,20 @@ +# +# +# 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. +# +# + +org.apache.dubbo diff --git a/dubbo-rpc/dubbo-rpc-rest/pom.xml b/dubbo-rpc/dubbo-rpc-rest/pom.xml index dc5855b88a..e94f81d7e5 100644 --- a/dubbo-rpc/dubbo-rpc-rest/pom.xml +++ b/dubbo-rpc/dubbo-rpc-rest/pom.xml @@ -125,12 +125,6 @@ provided - - jakarta.servlet - jakarta.servlet-api - provided - - io.netty netty-codec-http diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/BaseRestProtocolServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/BaseRestProtocolServer.java deleted file mode 100644 index baa0fff9cc..0000000000 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/BaseRestProtocolServer.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.rpc.protocol.rest; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.utils.StringUtils; - -import org.jboss.resteasy.spi.ResteasyDeployment; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; -import static org.apache.dubbo.rpc.protocol.rest.Constants.EXCEPTION_MAPPER_KEY; -import static org.apache.dubbo.rpc.protocol.rest.Constants.EXTENSION_KEY; - -public abstract class BaseRestProtocolServer implements RestProtocolServer { - - private String address; - - private final Map attributes = new ConcurrentHashMap<>(); - - @Override - public void start(URL url) { - getDeployment().getMediaTypeMappings().put("json", "application/json"); - getDeployment().getMediaTypeMappings().put("xml", "text/xml"); - getDeployment().getProviderClasses().add(RpcContextFilter.class.getName()); - - loadProviders(url.getParameter(EXCEPTION_MAPPER_KEY, RpcExceptionMapper.class.getName())); - - loadProviders(url.getParameter(EXTENSION_KEY, "")); - - doStart(url); - } - - @Override - public void deploy(Class resourceDef, Object resourceInstance, String contextPath) { - if (StringUtils.isEmpty(contextPath)) { - getDeployment().getRegistry().addResourceFactory(new DubboResourceFactory(resourceInstance, resourceDef)); - } else { - getDeployment().getRegistry().addResourceFactory(new DubboResourceFactory(resourceInstance, resourceDef), contextPath); - } - } - - @Override - public void undeploy(Class resourceDef) { - getDeployment().getRegistry().removeRegistrations(resourceDef); - } - - @Override - public String getAddress() { - return address; - } - - @Override - public void setAddress(String address) { - this.address = address; - } - - @Override - public Map getAttributes() { - return attributes; - } - - protected void loadProviders(String value) { - for (String clazz : COMMA_SPLIT_PATTERN.split(value)) { - if (!StringUtils.isEmpty(clazz)) { - getDeployment().getProviderClasses().add(clazz.trim()); - } - } - } - - protected abstract ResteasyDeployment getDeployment(); - - protected abstract void doStart(URL url); -} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/Constants.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/Constants.java index 8966315425..c2918062f4 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/Constants.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/Constants.java @@ -28,14 +28,13 @@ public interface Constants { String EXTENSION_KEY = "extension"; // http server - String SERVLET = "servlet"; - String JETTY = "jetty"; - - String TOMCAT = "tomcat"; - - String NETTY = "netty"; + String NETTY_HTTP = "netty_http"; // exception mapper String EXCEPTION_MAPPER_KEY = "exception.mapper"; + + + + } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/DubboHttpProtocolServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/DubboHttpProtocolServer.java deleted file mode 100644 index 475238d548..0000000000 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/DubboHttpProtocolServer.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.rpc.protocol.rest; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.remoting.http.HttpBinder; -import org.apache.dubbo.remoting.http.HttpHandler; -import org.apache.dubbo.remoting.http.HttpServer; -import org.apache.dubbo.remoting.http.servlet.BootstrapListener; -import org.apache.dubbo.remoting.http.servlet.ServletManager; -import org.apache.dubbo.rpc.RpcContext; -import org.apache.dubbo.rpc.RpcException; - -import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher; -import org.jboss.resteasy.spi.ResteasyDeployment; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Enumeration; - -public class DubboHttpProtocolServer extends BaseRestProtocolServer { - - private final HttpServletDispatcher dispatcher = new HttpServletDispatcher(); - private final ResteasyDeployment deployment = new ResteasyDeployment(); - private final HttpBinder httpBinder; - private HttpServer httpServer; - - public DubboHttpProtocolServer(HttpBinder httpBinder) { - this.httpBinder = httpBinder; - } - - @Override - protected void doStart(URL url) { - // TODO jetty will by default enable keepAlive so the xml config has no effect now - httpServer = httpBinder.bind(url, new RestHandler()); - - ServletContext servletContext = ServletManager.getInstance().getServletContext(url.getPort()); - if (servletContext == null) { - servletContext = ServletManager.getInstance().getServletContext(ServletManager.EXTERNAL_SERVER_PORT); - } - if (servletContext == null) { - throw new RpcException("No servlet context found. If you are using server='servlet', " + - "make sure that you've configured " + BootstrapListener.class.getName() + " in web.xml"); - } - - servletContext.setAttribute(ResteasyDeployment.class.getName(), deployment); - - try { - dispatcher.init(new SimpleServletConfig(servletContext)); - } catch (ServletException e) { - throw new RpcException(e); - } - } - - @Override - public void close() { - httpServer.close(); - } - - @Override - protected ResteasyDeployment getDeployment() { - return deployment; - } - - private class RestHandler implements HttpHandler { - - @Override - public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { - RpcContext.getServiceContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort()); - dispatcher.service(request, response); - } - } - - private static class SimpleServletConfig implements ServletConfig { - - private final ServletContext servletContext; - - public SimpleServletConfig(ServletContext servletContext) { - this.servletContext = servletContext; - } - - @Override - public String getServletName() { - return "DispatcherServlet"; - } - - @Override - public ServletContext getServletContext() { - return servletContext; - } - - @Override - public String getInitParameter(String s) { - return null; - } - - @Override - public Enumeration getInitParameterNames() { - return new Enumeration() { - @Override - public boolean hasMoreElements() { - return false; - } - - @Override - public String nextElement() { - return null; - } - }; - } - } -} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/DubboResourceFactory.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/DubboResourceFactory.java deleted file mode 100644 index ac8695ccc4..0000000000 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/DubboResourceFactory.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.rpc.protocol.rest; - -import org.jboss.resteasy.spi.HttpRequest; -import org.jboss.resteasy.spi.HttpResponse; -import org.jboss.resteasy.spi.ResourceFactory; -import org.jboss.resteasy.spi.ResteasyProviderFactory; - -/** - * We don't support propertyInjector here since the resource impl should be singleton in dubbo - */ -public class DubboResourceFactory implements ResourceFactory { - - private final Object resourceInstance; - private final Class scannableClass; - - public DubboResourceFactory(Object resourceInstance, Class scannableClass) { - this.resourceInstance = resourceInstance; - this.scannableClass = scannableClass; - } - - @Override - public Object createResource(HttpRequest request, HttpResponse response, - ResteasyProviderFactory factory) { - return resourceInstance; - } - - @Override - public Class getScannableClass() { - return scannableClass; - } - - @Override - public void registered(ResteasyProviderFactory factory) { - } - - @Override - public void requestFinished(HttpRequest request, HttpResponse response, - Object resource) { - } - - @Override - public void unregistered() { - } -} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyHttpRestServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyHttpRestServer.java new file mode 100644 index 0000000000..d65c368ce6 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyHttpRestServer.java @@ -0,0 +1,213 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest; + +import io.netty.buffer.PooledByteBufAllocator; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelOption; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.metadata.rest.PathMatcher; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.metadata.rest.ServiceRestMetadata; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; +import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionMapper; +import org.apache.dubbo.rpc.protocol.rest.handler.NettyHttpHandler; +import org.apache.dubbo.rpc.protocol.rest.netty.NettyServer; +import org.apache.dubbo.rpc.protocol.rest.netty.RestHttpRequestDecoder; +import org.apache.dubbo.rpc.protocol.rest.netty.UnSharedHandlerCreator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static org.apache.dubbo.common.constants.CommonConstants.BACKLOG_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; +import static org.apache.dubbo.common.constants.CommonConstants.IO_THREADS_KEY; +import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY; +import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY; +import static org.apache.dubbo.remoting.Constants.DEFAULT_IO_THREADS; + +/** + * netty http server + */ +public class NettyHttpRestServer implements RestProtocolServer { + + private final PathAndInvokerMapper pathAndInvokerMapper = new PathAndInvokerMapper(); + private final ExceptionMapper exceptionMapper = new ExceptionMapper(); + private NettyServer server = getNettyServer(); + + /** + * for triple override + * + * @return + */ + protected NettyServer getNettyServer() { + return new NettyServer(); + } + + private String address; + + private final Map attributes = new ConcurrentHashMap<>(); + + + @Override + public String getAddress() { + return address; + } + + @Override + public void setAddress(String address) { + + this.address = address; + } + + @Override + public void close() { + server.stop(); + + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public void start(URL url) { + + registerExceptionMapper(url); + + String bindIp = url.getParameter(BIND_IP_KEY, url.getHost()); + if (!url.isAnyHost() && NetUtils.isValidLocalHost(bindIp)) { + server.setHostname(bindIp); + } + server.setPort(url.getParameter(BIND_PORT_KEY, url.getPort())); + + // child options + server.setChildChannelOptions(getChildChannelOptionMap(url)); + + // set options + server.setChannelOptions(getChannelOptionMap(url)); + // set unshared callback + server.setUnSharedHandlerCallBack(getUnSharedHttpChannelHandlers()); + // set channel handler and @Shared + server.setChannelHandlers(getChannelHandlers(url)); + server.setIoWorkerCount(url.getParameter(IO_THREADS_KEY, DEFAULT_IO_THREADS)); + server.start(url); + } + + private UnSharedHandlerCreator getUnSharedHttpChannelHandlers() { + return new UnSharedHandlerCreator() { + @Override + public List getUnSharedHandlers(URL url) { + return Arrays.asList( + // TODO add SslServerTlsHandler +// channelPipeline.addLast(ch.pipeline().addLast("negotiation", new SslServerTlsHandler(url))); + + new HttpRequestDecoder( + url.getParameter(RestConstant.MAX_INITIAL_LINE_LENGTH_PARAM, RestConstant.MAX_INITIAL_LINE_LENGTH), + url.getParameter(RestConstant.MAX_HEADER_SIZE_PARAM, RestConstant.MAX_HEADER_SIZE), + url.getParameter(RestConstant.MAX_CHUNK_SIZE_PARAM, RestConstant.MAX_CHUNK_SIZE)), + new HttpObjectAggregator(url.getParameter(RestConstant.MAX_REQUEST_SIZE_PARAM, RestConstant.MAX_REQUEST_SIZE)), + new HttpResponseEncoder(), new RestHttpRequestDecoder(new NettyHttpHandler(pathAndInvokerMapper, exceptionMapper), url)); + } + }; + } + + /** + * create child channel options map + * + * @param url + * @return + */ + protected Map getChildChannelOptionMap(URL url) { + Map channelOption = new HashMap<>(); + channelOption.put(ChannelOption.SO_KEEPALIVE, url.getParameter(Constants.KEEP_ALIVE_KEY, Constants.DEFAULT_KEEP_ALIVE)); + return channelOption; + } + + /** + * create channel options map + * + * @param url + * @return + */ + protected Map getChannelOptionMap(URL url) { + Map options = new HashMap<>(); + + options.put(ChannelOption.SO_REUSEADDR, Boolean.TRUE); + options.put(ChannelOption.TCP_NODELAY, Boolean.TRUE); + options.put(ChannelOption.SO_BACKLOG, url.getPositiveParameter(BACKLOG_KEY, org.apache.dubbo.remoting.Constants.DEFAULT_BACKLOG)); + options.put(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); + return options; + } + + /** + * create channel handler + * + * @param url + * @return + */ + protected List getChannelHandlers(URL url) { + List channelHandlers = new ArrayList<>(); + + return channelHandlers; + } + + @Override + public void deploy(ServiceRestMetadata serviceRestMetadata, Invoker invoker) { + Map pathToServiceMapContainPathVariable = + serviceRestMetadata.getPathContainPathVariableToServiceMap(); + pathAndInvokerMapper.addPathAndInvoker(pathToServiceMapContainPathVariable, invoker); + + Map pathToServiceMapUnContainPathVariable = + serviceRestMetadata.getPathUnContainPathVariableToServiceMap(); + pathAndInvokerMapper.addPathAndInvoker(pathToServiceMapUnContainPathVariable, invoker); + } + + @Override + public void undeploy(ServiceRestMetadata serviceRestMetadata) { + Map pathToServiceMapContainPathVariable = + serviceRestMetadata.getPathContainPathVariableToServiceMap(); + pathToServiceMapContainPathVariable.keySet().stream().forEach(pathAndInvokerMapper::removePath); + + Map pathToServiceMapUnContainPathVariable = + serviceRestMetadata.getPathUnContainPathVariableToServiceMap(); + pathToServiceMapUnContainPathVariable.keySet().stream().forEach(pathAndInvokerMapper::removePath); + + } + + private void registerExceptionMapper(URL url) { + + for (String clazz : COMMA_SPLIT_PATTERN.split(url.getParameter(Constants.EXTENSION_KEY, RpcExceptionMapper.class.getName()))) { + if (!StringUtils.isEmpty(clazz)) { + exceptionMapper.registerMapper(clazz); + } + } + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyRestProtocolServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyRestProtocolServer.java deleted file mode 100644 index fbb9be782e..0000000000 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyRestProtocolServer.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.rpc.protocol.rest; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.utils.NetUtils; - -import io.netty.channel.ChannelOption; -import org.jboss.resteasy.plugins.server.netty.NettyJaxrsServer; -import org.jboss.resteasy.spi.ResteasyDeployment; - -import java.util.HashMap; -import java.util.Map; - -import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREADS; -import static org.apache.dubbo.common.constants.CommonConstants.IO_THREADS_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; -import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY; -import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY; -import static org.apache.dubbo.remoting.Constants.DEFAULT_IO_THREADS; -import static org.apache.dubbo.remoting.Constants.DEFAULT_PAYLOAD; -import static org.apache.dubbo.remoting.Constants.PAYLOAD_KEY; -import static org.apache.dubbo.rpc.protocol.rest.Constants.DEFAULT_KEEP_ALIVE; -import static org.apache.dubbo.rpc.protocol.rest.Constants.KEEP_ALIVE_KEY; - -/** - * Netty server can't support @Context injection of servlet objects since it's not a servlet container - * - */ -public class NettyRestProtocolServer extends BaseRestProtocolServer { - - private final NettyJaxrsServer server = new NettyJaxrsServer(); - - @Override - @SuppressWarnings("rawtypes") - protected void doStart(URL url) { - String bindIp = url.getParameter(BIND_IP_KEY, url.getHost()); - if (!url.isAnyHost() && NetUtils.isValidLocalHost(bindIp)) { - server.setHostname(bindIp); - } - server.setPort(url.getParameter(BIND_PORT_KEY, url.getPort())); - Map channelOption = new HashMap<>(); - channelOption.put(ChannelOption.SO_KEEPALIVE, url.getParameter(KEEP_ALIVE_KEY, DEFAULT_KEEP_ALIVE)); - server.setChildChannelOptions(channelOption); - server.setExecutorThreadCount(url.getParameter(THREADS_KEY, DEFAULT_THREADS)); - server.setIoWorkerCount(url.getParameter(IO_THREADS_KEY, DEFAULT_IO_THREADS)); - server.setMaxRequestSize(url.getParameter(PAYLOAD_KEY, DEFAULT_PAYLOAD)); - server.start(); - } - - @Override - public void close() { - server.stop(); - } - - @Override - protected ResteasyDeployment getDeployment() { - return server.getDeployment(); - } -} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/PathAndInvokerMapper.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/PathAndInvokerMapper.java new file mode 100644 index 0000000000..ce3a187e70 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/PathAndInvokerMapper.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest; + +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.metadata.rest.PathMatcher; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.protocol.rest.exception.DoublePathCheckException; +import org.apache.dubbo.rpc.protocol.rest.exception.PathNoFoundException; +import org.apache.dubbo.rpc.protocol.rest.pair.InvokerAndRestMethodMetadataPair; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * save the path & metadata info mapping + */ +public class PathAndInvokerMapper { + private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(PathAndInvokerMapper.class); + + private final Map pathToServiceMapContainPathVariable = new ConcurrentHashMap<>(); + private final Map pathToServiceMapNoPathVariable = new ConcurrentHashMap<>(); + + + /** + * deploy path metadata + * + * @param metadataMap + * @param invoker + */ + public void addPathAndInvoker(Map metadataMap, Invoker invoker) { + + metadataMap.entrySet().stream().forEach(entry -> { + PathMatcher pathMatcher = entry.getKey(); + if (pathMatcher.hasPathVariable()) { + addPathMatcherToPathMap(pathMatcher, pathToServiceMapContainPathVariable, InvokerAndRestMethodMetadataPair.pair(invoker, entry.getValue())); + } else { + addPathMatcherToPathMap(pathMatcher, pathToServiceMapNoPathVariable, InvokerAndRestMethodMetadataPair.pair(invoker, entry.getValue())); + } + }); + } + + /** + * acquire metadata & invoker by service info + * + * @param path + * @param version + * @param group + * @param port + * @return + */ + public InvokerAndRestMethodMetadataPair getRestMethodMetadata(String path, String version, String group, Integer port) { + + + PathMatcher pathMather = PathMatcher.getInvokeCreatePathMatcher(path, version, group, port); + + // first search from pathToServiceMapNoPathVariable + if (pathToServiceMapNoPathVariable.containsKey(pathMather)) { + return pathToServiceMapNoPathVariable.get(pathMather); + } + + // second search from pathToServiceMapNoPathVariable + if (pathToServiceMapContainPathVariable.containsKey(pathMather)) { + return pathToServiceMapContainPathVariable.get(pathMather); + } + + throw new PathNoFoundException("rest service Path no found, current path info:" + pathMather); + } + + /** + * undeploy path metadata + * + * @param pathMatcher + */ + public void removePath(PathMatcher pathMatcher) { + + InvokerAndRestMethodMetadataPair containPathVariablePair = pathToServiceMapContainPathVariable.remove(pathMatcher); + + InvokerAndRestMethodMetadataPair unContainPathVariablePair = pathToServiceMapNoPathVariable.remove(pathMatcher); + logger.info("dubbo rest undeploy pathMatcher:" + pathMatcher + + ", and path variable method is :" + (containPathVariablePair == null ? null : containPathVariablePair.getRestMethodMetadata().getReflectMethod()) + + ", and no path variable method is :" + (unContainPathVariablePair == null ? null : unContainPathVariablePair.getRestMethodMetadata().getReflectMethod())); + + + } + + public void addPathMatcherToPathMap(PathMatcher pathMatcher, + Map pathMatcherPairMap, + InvokerAndRestMethodMetadataPair invokerRestMethodMetadataPair) { + + if (pathMatcherPairMap.containsKey(pathMatcher)) { + + InvokerAndRestMethodMetadataPair beforeMetadata = pathMatcherPairMap.get(pathMatcher); + + throw new DoublePathCheckException( + "dubbo rest double path check error, current path is: " + pathMatcher + + " ,and service method is: " + invokerRestMethodMetadataPair.getRestMethodMetadata().getReflectMethod() + + "before service method is: " + beforeMetadata.getRestMethodMetadata().getReflectMethod() + ); + } + + pathMatcherPairMap.put(pathMatcher, invokerRestMethodMetadataPair); + + + logger.info("dubbo rest deploy pathMatcher:" + pathMatcher + ", and service method is :" + invokerRestMethodMetadataPair.getRestMethodMetadata().getReflectMethod()); + } + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestHeaderEnum.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestHeaderEnum.java new file mode 100644 index 0000000000..9674394359 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestHeaderEnum.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest; + +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; + +public enum RestHeaderEnum { + CONTENT_TYPE(RestConstant.CONTENT_TYPE), + ACCEPT(RestConstant.ACCEPT), + GROUP(RestConstant.REST_HEADER_PREFIX + RestConstant.GROUP), + VERSION(RestConstant.REST_HEADER_PREFIX + RestConstant.VERSION), + PATH(RestConstant.REST_HEADER_PREFIX + RestConstant.PATH), + KEEP_ALIVE_HEADER(RestConstant.KEEP_ALIVE_HEADER), + CONNECTION(RestConstant.CONNECTION), + REST_HEADER_PREFIX(RestConstant.REST_HEADER_PREFIX), + TOKEN_KEY(RestConstant.REST_HEADER_PREFIX + RestConstant.TOKEN_KEY), + + + ; + private final String header; + + RestHeaderEnum(String header) { + this.header = header; + } + + public String getHeader() { + return header; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestInvoker.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestInvoker.java new file mode 100644 index 0000000000..09266c950a --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestInvoker.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest; + + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.metadata.ParameterTypesComparator; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.metadata.rest.ServiceRestMetadata; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.remoting.http.RestClient; +import org.apache.dubbo.remoting.http.RestResult; +import org.apache.dubbo.rpc.AppResponse; +import org.apache.dubbo.rpc.AsyncRpcResult; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.protocol.AbstractInvoker; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept; +import org.apache.dubbo.rpc.protocol.rest.exception.ParamParseException; +import org.apache.dubbo.rpc.protocol.rest.exception.PathNoFoundException; +import org.apache.dubbo.rpc.protocol.rest.exception.RemoteServerInternalException; +import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager; +import org.apache.dubbo.rpc.protocol.rest.util.HttpHeaderUtil; +import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +public class RestInvoker extends AbstractInvoker { + private final ServiceRestMetadata serviceRestMetadata; + private final ReferenceCountedClient referenceCountedClient; + private final Set httpConnectionPreBuildIntercepts; + + + public RestInvoker(Class type, URL url, + ReferenceCountedClient referenceCountedClient, + Set httpConnectionPreBuildIntercepts, + ServiceRestMetadata serviceRestMetadata) { + super(type, url); + this.serviceRestMetadata = serviceRestMetadata; + this.referenceCountedClient = referenceCountedClient; + this.httpConnectionPreBuildIntercepts = httpConnectionPreBuildIntercepts; + } + + @Override + protected Result doInvoke(Invocation invocation) { + try { + + Map> metadataMap = serviceRestMetadata.getMethodToServiceMap(); + // get metadata + RestMethodMetadata restMethodMetadata = metadataMap.get(invocation.getMethodName()).get(ParameterTypesComparator.getInstance(invocation.getParameterTypes())); + + // create requestTemplate + RequestTemplate requestTemplate = new RequestTemplate(invocation, restMethodMetadata.getRequest().getMethod(), getUrl().getAddress()); + + HttpConnectionCreateContext httpConnectionCreateContext = + creatHttpConnectionCreateContext(invocation, serviceRestMetadata, restMethodMetadata, requestTemplate); + + // fill real data + for (HttpConnectionPreBuildIntercept intercept : httpConnectionPreBuildIntercepts) { + intercept.intercept(httpConnectionCreateContext); + } + + // TODO check rest client cannot be reused + CompletableFuture future = referenceCountedClient.getClient().send(requestTemplate); + CompletableFuture responseFuture = new CompletableFuture<>(); + AsyncRpcResult asyncRpcResult = new AsyncRpcResult(responseFuture, invocation); + future.whenComplete((r, t) -> { + if (t != null) { + responseFuture.completeExceptionally(t); + } else { + AppResponse appResponse = new AppResponse(); + try { + int responseCode = r.getResponseCode(); + MediaType mediaType = MediaType.TEXT_PLAIN; + + if (responseCode == 404) { + responseFuture.completeExceptionally(new PathNoFoundException(r.getMessage())); + } else if (400 <= responseCode && responseCode < 500) { + responseFuture.completeExceptionally(new ParamParseException(r.getMessage())); + // TODO add Exception Mapper + } else if (responseCode >= 500) { + responseFuture.completeExceptionally(new RemoteServerInternalException(r.getMessage())); + } else if (responseCode < 400) { + mediaType = MediaTypeUtil.convertMediaType(restMethodMetadata.getReflectMethod().getReturnType(), r.getContentType()); + Object value = HttpMessageCodecManager.httpMessageDecode(r.getBody(), + restMethodMetadata.getReflectMethod().getReturnType(), mediaType); + appResponse.setValue(value); + // resolve response attribute & attachment + HttpHeaderUtil.parseResponseHeader(appResponse, r); + responseFuture.complete(appResponse); + } + } catch (Exception e) { + responseFuture.completeExceptionally(e); + } + } + }); + return asyncRpcResult; + } catch (RpcException e) { + throw e; + } + } + + /** + * create intercept context + * + * @param invocation + * @param serviceRestMetadata + * @param restMethodMetadata + * @param requestTemplate + * @return + */ + private HttpConnectionCreateContext creatHttpConnectionCreateContext(Invocation invocation, ServiceRestMetadata serviceRestMetadata, RestMethodMetadata restMethodMetadata, RequestTemplate requestTemplate) { + HttpConnectionCreateContext httpConnectionCreateContext = new HttpConnectionCreateContext(); + httpConnectionCreateContext.setRequestTemplate(requestTemplate); + httpConnectionCreateContext.setRestMethodMetadata(restMethodMetadata); + httpConnectionCreateContext.setServiceRestMetadata(serviceRestMetadata); + httpConnectionCreateContext.setInvocation(invocation); + httpConnectionCreateContext.setUrl(getUrl()); + return httpConnectionCreateContext; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java index 6884aacc14..ad273efda3 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java @@ -18,60 +18,36 @@ package org.apache.dubbo.rpc.protocol.rest; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; -import org.apache.dubbo.common.utils.StringUtils; -import org.apache.dubbo.metadata.ParameterTypesComparator; -import org.apache.dubbo.metadata.rest.RestMethodMetadata; -import org.apache.dubbo.metadata.rest.media.MediaType; -import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.metadata.rest.ServiceRestMetadata; import org.apache.dubbo.remoting.http.RestClient; -import org.apache.dubbo.remoting.http.RestResult; import org.apache.dubbo.remoting.http.factory.RestClientFactory; -import org.apache.dubbo.remoting.http.servlet.BootstrapListener; -import org.apache.dubbo.remoting.http.servlet.ServletManager; -import org.apache.dubbo.rpc.AppResponse; -import org.apache.dubbo.rpc.AsyncRpcResult; -import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.ProtocolServer; -import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.FrameworkModel; -import org.apache.dubbo.rpc.protocol.AbstractInvoker; -import org.apache.dubbo.rpc.protocol.AbstractProxyProtocol; -import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionConfig; -import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext; +import org.apache.dubbo.rpc.protocol.AbstractExporter; +import org.apache.dubbo.rpc.protocol.AbstractProtocol; import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept; import org.apache.dubbo.rpc.protocol.rest.annotation.metadata.MetadataResolver; -import org.apache.dubbo.rpc.protocol.rest.exception.HttpClientException; -import org.apache.dubbo.rpc.protocol.rest.exception.RemoteServerInternalException; -import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager; -import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil; -import org.jboss.resteasy.util.GetRestful; -import javax.servlet.ServletContext; -import javax.ws.rs.ProcessingException; -import javax.ws.rs.WebApplicationException; import java.util.Map; +import java.util.Objects; import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.stream.Collectors; -import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_CLIENT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_SERVER; import static org.apache.dubbo.remoting.Constants.SERVER_KEY; -import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; import static org.apache.dubbo.rpc.protocol.rest.constans.RestConstant.PATH_SEPARATOR; -public class RestProtocol extends AbstractProxyProtocol { +public class RestProtocol extends AbstractProtocol { private static final int DEFAULT_PORT = 80; - private static final String DEFAULT_CLIENT = org.apache.dubbo.remoting.Constants.OK_HTTP; - private static final String DEFAULT_SERVER = Constants.JETTY; + private static final String DEFAULT_SERVER = Constants.NETTY_HTTP; private final RestServerFactory serverFactory = new RestServerFactory(); @@ -82,7 +58,6 @@ public class RestProtocol extends AbstractProxyProtocol { private final Set httpConnectionPreBuildIntercepts; public RestProtocol(FrameworkModel frameworkModel) { - super(WebApplicationException.class, ProcessingException.class); this.clientFactory = frameworkModel.getExtensionLoader(RestClientFactory.class).getAdaptiveExtension(); this.httpConnectionPreBuildIntercepts = frameworkModel.getExtensionLoader(HttpConnectionPreBuildIntercept.class).getSupportedExtensionInstances(); } @@ -93,48 +68,49 @@ public class RestProtocol extends AbstractProxyProtocol { return DEFAULT_PORT; } + @Override - protected Runnable doExport(T impl, Class type, URL url) throws RpcException { - String addr = getAddr(url); - Class implClass = url.getServiceModel().getProxyObject().getClass(); - RestProtocolServer server = (RestProtocolServer) ConcurrentHashMapUtils.computeIfAbsent(serverMap, addr, restServer -> { + @SuppressWarnings("unchecked") + public Exporter export(final Invoker invoker) throws RpcException { + URL url = invoker.getUrl(); + final String uri = serviceKey(url); + Exporter exporter = (Exporter) exporterMap.get(uri); + if (exporter != null) { + // When modifying the configuration through override, you need to re-expose the newly modified service. + if (Objects.equals(exporter.getInvoker().getUrl(), invoker.getUrl())) { + return exporter; + } + } + + + // resolve metadata + ServiceRestMetadata serviceRestMetadata = + MetadataResolver.resolveProviderServiceMetadata(url.getServiceModel().getProxyObject().getClass(), + url, getContextPath(url)); + + + // TODO add Extension filter + // create rest server + RestProtocolServer server = (RestProtocolServer) ConcurrentHashMapUtils.computeIfAbsent(serverMap, getAddr(url), restServer -> { RestProtocolServer s = serverFactory.createServer(url.getParameter(SERVER_KEY, DEFAULT_SERVER)); s.setAddress(url.getAddress()); s.start(url); return s; }); - String contextPath = getContextPath(url); - if (Constants.SERVLET.equalsIgnoreCase(url.getParameter(SERVER_KEY, DEFAULT_SERVER))) { - ServletContext servletContext = ServletManager.getInstance().getServletContext(ServletManager.EXTERNAL_SERVER_PORT); - if (servletContext == null) { - throw new RpcException("No servlet context found. Since you are using server='servlet', " + - "make sure that you've configured " + BootstrapListener.class.getName() + " in web.xml"); + + server.deploy(serviceRestMetadata, invoker); + + exporter = new AbstractExporter(invoker) { + @Override + public void afterUnExport() { + destroyInternal(url); + exporterMap.remove(uri); + server.undeploy(serviceRestMetadata); } - String webappPath = servletContext.getContextPath(); - if (StringUtils.isNotEmpty(webappPath)) { - webappPath = webappPath.substring(1); - if (!contextPath.startsWith(webappPath)) { - throw new RpcException("Since you are using server='servlet', " + - "make sure that the 'contextpath' property starts with the path of external webapp"); - } - contextPath = contextPath.substring(webappPath.length()); - if (contextPath.startsWith(PATH_SEPARATOR)) { - contextPath = contextPath.substring(1); - } - } - } - - final Class resourceDef = GetRestful.getRootResourceClass(implClass) != null ? implClass : type; - - server.deploy(resourceDef, impl, contextPath); - - final RestProtocolServer s = server; - return () -> { - // TODO due to dubbo's current architecture, - // it will be called from registry protocol in the shutdown process and won't appear in logs - s.undeploy(resourceDef); }; + exporterMap.put(uri, exporter); + return exporter; } @@ -146,108 +122,42 @@ public class RestProtocol extends AbstractProxyProtocol { synchronized (clients) { refClient = clients.get(url.getAddress()); if (refClient == null || refClient.isDestroyed()) { - refClient = ConcurrentHashMapUtils.computeIfAbsent(clients, url.getAddress(), _key -> createReferenceCountedClient(url,clients)); + refClient = ConcurrentHashMapUtils.computeIfAbsent(clients, url.getAddress(), _key -> createReferenceCountedClient(url)); } } } refClient.retain(); + + String contextPathFromUrl = getContextPath(url); + // resolve metadata - Map> metadataMap = MetadataResolver.resolveConsumerServiceMetadata(type, url); + ServiceRestMetadata serviceRestMetadata = + MetadataResolver.resolveConsumerServiceMetadata(type, url, contextPathFromUrl); - ReferenceCountedClient finalRefClient = refClient; - Invoker invoker = new AbstractInvoker(type, url, new String[]{INTERFACE_KEY, GROUP_KEY, TOKEN_KEY}) { - @Override - protected Result doInvoke(Invocation invocation) { - try { - RestMethodMetadata restMethodMetadata = metadataMap.get(invocation.getMethodName()).get(ParameterTypesComparator.getInstance(invocation.getParameterTypes())); + Invoker invoker = new RestInvoker(type, url, + refClient, httpConnectionPreBuildIntercepts, serviceRestMetadata); - RequestTemplate requestTemplate = new RequestTemplate(invocation, restMethodMetadata.getRequest().getMethod(), url.getAddress(), getContextPath(url)); - - HttpConnectionCreateContext httpConnectionCreateContext = new HttpConnectionCreateContext(); - // TODO dynamic load config - httpConnectionCreateContext.setConnectionConfig(new HttpConnectionConfig()); - httpConnectionCreateContext.setRequestTemplate(requestTemplate); - httpConnectionCreateContext.setRestMethodMetadata(restMethodMetadata); - httpConnectionCreateContext.setInvocation(invocation); - httpConnectionCreateContext.setUrl(url); - - for (HttpConnectionPreBuildIntercept intercept : httpConnectionPreBuildIntercepts) { - intercept.intercept(httpConnectionCreateContext); - } - - - CompletableFuture future = finalRefClient.getClient().send(requestTemplate); - CompletableFuture responseFuture = new CompletableFuture<>(); - AsyncRpcResult asyncRpcResult = new AsyncRpcResult(responseFuture, invocation); - future.whenComplete((r, t) -> { - if (t != null) { - responseFuture.completeExceptionally(t); - } else { - AppResponse appResponse = new AppResponse(); - try { - int responseCode = r.getResponseCode(); - MediaType mediaType = MediaType.TEXT_PLAIN; - - if (400 < responseCode && responseCode < 500) { - throw new HttpClientException(r.getMessage()); - } else if (responseCode >= 500) { - throw new RemoteServerInternalException(r.getMessage()); - } else if (responseCode < 400) { - mediaType = MediaTypeUtil.convertMediaType(r.getContentType()); - } - - - Object value = HttpMessageCodecManager.httpMessageDecode(r.getBody(), - restMethodMetadata.getReflectMethod().getReturnType(), mediaType); - appResponse.setValue(value); - Map headers = r.headers() - .entrySet() - .stream() - .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0))); - appResponse.setAttachments(headers); - responseFuture.complete(appResponse); - } catch (Exception e) { - responseFuture.completeExceptionally(e); - } - } - }); - return asyncRpcResult; - } catch (RpcException e) { - if (e.getCode() == RpcException.UNKNOWN_EXCEPTION) { - e.setCode(getErrorCode(e.getCause())); - } - throw e; - } - } - - @Override - public void destroy() { - super.destroy(); - invokers.remove(this); - destroyInternal(url); - } - }; invokers.add(invoker); return invoker; } - private ReferenceCountedClient createReferenceCountedClient(URL url, ConcurrentMap> clients) throws RpcException { + /** + * create rest ReferenceCountedClient + * + * @param url + * @return + * @throws RpcException + */ + private ReferenceCountedClient createReferenceCountedClient(URL url) throws RpcException { // url -> RestClient RestClient restClient = clientFactory.createRestClient(url); - ReferenceCountedClient refClient = new ReferenceCountedClient(restClient, clients, clientFactory, url); - - return refClient; + return new ReferenceCountedClient<>(restClient, clients, clientFactory, url); } - @Override - protected int getErrorCode(Throwable e) { - // TODO - return super.getErrorCode(e); - } @Override public void destroy() { @@ -289,7 +199,7 @@ public class RestProtocol extends AbstractProxyProtocol { * * @return return path only if user has explicitly gave then a value. */ - protected String getContextPath(URL url) { + private String getContextPath(URL url) { String contextPath = url.getPath(); if (contextPath != null) { if (contextPath.equalsIgnoreCase(url.getParameter(INTERFACE_KEY))) { @@ -304,8 +214,8 @@ public class RestProtocol extends AbstractProxyProtocol { } } - @Override - protected void destroyInternal(URL url) { + + private void destroyInternal(URL url) { try { ReferenceCountedClient referenceCountedClient = clients.get(url.getAddress()); if (referenceCountedClient != null && referenceCountedClient.release()) { diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolServer.java index ee03685662..4de74c937d 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolServer.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolServer.java @@ -17,17 +17,18 @@ package org.apache.dubbo.rpc.protocol.rest; import org.apache.dubbo.common.URL; +import org.apache.dubbo.metadata.rest.ServiceRestMetadata; +import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.ProtocolServer; + public interface RestProtocolServer extends ProtocolServer { void start(URL url); - /** - * @param resourceDef it could be either resource interface or resource impl - */ - void deploy(Class resourceDef, Object resourceInstance, String contextPath); - void undeploy(Class resourceDef); + void deploy(ServiceRestMetadata serviceRestMetadata, Invoker invoker); + + void undeploy(ServiceRestMetadata serviceRestMetadata); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestRPCInvocationUtil.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestRPCInvocationUtil.java new file mode 100644 index 0000000000..8388e1fe88 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestRPCInvocationUtil.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest; + +import org.apache.dubbo.common.BaseServiceMetadata; +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.protocol.rest.annotation.ParamParserManager; +import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.ProviderParseContext; +import org.apache.dubbo.rpc.protocol.rest.exception.ParamParseException; +import org.apache.dubbo.rpc.protocol.rest.pair.InvokerAndRestMethodMetadataPair; +import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade; +import org.apache.dubbo.rpc.protocol.rest.util.HttpHeaderUtil; + + +import java.util.Arrays; +import java.util.List; + + +public class RestRPCInvocationUtil { + + private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RestRPCInvocationUtil.class); + + /** + * service method real args parse + * + * @param rpcInvocation + * @param request + * @param servletRequest + * @param servletResponse + * @param restMethodMetadata + */ + public static void parseMethodArgs(RpcInvocation rpcInvocation, RequestFacade request, Object servletRequest, + Object servletResponse, + RestMethodMetadata restMethodMetadata) { + + try { + ProviderParseContext parseContext = createParseContext(request, servletRequest, servletResponse, restMethodMetadata); + Object[] args = ParamParserManager.providerParamParse(parseContext); + + List argInfos = parseContext.getArgInfos(); + + for (ArgInfo argInfo : argInfos) { + // TODO set default value + if (argInfo.getParamType().isPrimitive() && args[argInfo.getIndex()] == null) { + throw new ParamParseException("\n dubbo provider primitive arg not exist in request, method is: " + + restMethodMetadata.getReflectMethod() + "\n type is: " + + argInfo.getParamType() + " \n and arg index is: " + argInfo.getIndex()); + } + } + + rpcInvocation.setArguments(args); + } catch (Exception e) { + logger.error("", e.getMessage(), "", "dubbo rest provider method args parse error: ", e); + throw new ParamParseException(e.getMessage()); + } + } + + /** + * create parseMethodArgs context + * + * @param request + * @param servletRequest + * @param servletResponse + * @param restMethodMetadata + * @return + */ + private static ProviderParseContext createParseContext(RequestFacade request, Object servletRequest, Object servletResponse, RestMethodMetadata restMethodMetadata) { + ProviderParseContext parseContext = new ProviderParseContext(request); + parseContext.setResponse(servletResponse); + parseContext.setRequest(servletRequest); + + Object[] objects = new Object[restMethodMetadata.getArgInfos().size()]; + parseContext.setArgs(Arrays.asList(objects)); + parseContext.setArgInfos(restMethodMetadata.getArgInfos()); + + + return parseContext; + } + + /** + * build RpcInvocation + * + * @param request + * @param restMethodMetadata + * @return + */ + public static RpcInvocation createBaseRpcInvocation(RequestFacade request, RestMethodMetadata restMethodMetadata) { + RpcInvocation rpcInvocation = new RpcInvocation(); + + rpcInvocation.setParameterTypes(restMethodMetadata.getReflectMethod().getParameterTypes()); + rpcInvocation.setReturnType(restMethodMetadata.getReflectMethod().getReturnType()); + rpcInvocation.setMethodName(restMethodMetadata.getMethod().getName()); + + // TODO set protocolServiceKey ,but no set method +// + + HttpHeaderUtil.parseRequest(rpcInvocation, request); + + String serviceKey = BaseServiceMetadata.buildServiceKey(request.getHeader(RestHeaderEnum.PATH.getHeader()), + request.getHeader(RestHeaderEnum.GROUP.getHeader()), + request.getHeader(RestHeaderEnum.VERSION.getHeader())); + rpcInvocation.setTargetServiceUniqueName(serviceKey); + + return rpcInvocation; + } + + + /** + * get path mapping + * + * @param request + * @param pathAndInvokerMapper + * @return + */ + public static InvokerAndRestMethodMetadataPair getRestMethodMetadata(RequestFacade request, PathAndInvokerMapper pathAndInvokerMapper) { + String path = request.getPath(); + String version = request.getHeader(RestHeaderEnum.VERSION.getHeader()); + String group = request.getHeader(RestHeaderEnum.GROUP.getHeader()); + + return pathAndInvokerMapper.getRestMethodMetadata(path, version, group, null); + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestServerFactory.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestServerFactory.java index 01c0df5aed..367c3a0195 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestServerFactory.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestServerFactory.java @@ -16,8 +16,6 @@ */ package org.apache.dubbo.rpc.protocol.rest; -import org.apache.dubbo.remoting.http.HttpBinder; -import org.apache.dubbo.rpc.model.FrameworkModel; /** * Only the server that implements servlet container @@ -25,15 +23,8 @@ import org.apache.dubbo.rpc.model.FrameworkModel; */ public class RestServerFactory { - private static final HttpBinder httpBinder = FrameworkModel.defaultModel().getAdaptiveExtension(HttpBinder.class); public RestProtocolServer createServer(String name) { - if (Constants.SERVLET.equalsIgnoreCase(name) || Constants.JETTY.equalsIgnoreCase(name) || Constants.TOMCAT.equalsIgnoreCase(name)) { - return new DubboHttpProtocolServer(httpBinder); - } else if (Constants.NETTY.equalsIgnoreCase(name)) { - return new NettyRestProtocolServer(); - } else { - throw new IllegalArgumentException("Unrecognized server name: " + name); - } + return new NettyHttpRestServer(); } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcContextFilter.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcContextFilter.java deleted file mode 100644 index 3572ea67e3..0000000000 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcContextFilter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.rpc.protocol.rest; - -import org.apache.dubbo.common.constants.CommonConstants; -import org.apache.dubbo.common.utils.CollectionUtils; -import org.apache.dubbo.common.utils.StringUtils; -import org.apache.dubbo.rpc.Invocation; -import org.apache.dubbo.rpc.RpcContext; - -import org.jboss.resteasy.spi.ResteasyProviderFactory; - -import javax.annotation.Priority; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.ws.rs.client.ClientRequestContext; -import javax.ws.rs.client.ClientRequestFilter; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerRequestFilter; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; - -@Priority(Integer.MIN_VALUE + 1) -public class RpcContextFilter implements ContainerRequestFilter, ClientRequestFilter { - - private static final String DUBBO_ATTACHMENT_HEADER = "Dubbo-Attachments"; - - // currently, we use a single header to hold the attachments so that the total attachment size limit is about 8k - private static final int MAX_HEADER_SIZE = 8 * 1024; - - @Override - public void filter(ContainerRequestContext requestContext) throws IOException { - HttpServletRequest request = ResteasyProviderFactory.getContextData(HttpServletRequest.class); - RpcContext.getServiceContext().setRequest(request); - - // this only works for servlet containers - if (request != null && RpcContext.getServiceContext().getRemoteAddress() == null) { - RpcContext.getServiceContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort()); - } - - RpcContext.getServiceContext().setResponse(ResteasyProviderFactory.getContextData(HttpServletResponse.class)); - - String headers = requestContext.getHeaderString(DUBBO_ATTACHMENT_HEADER); - if (headers != null) { - for (String header : headers.split(CommonConstants.COMMA_SEPARATOR)) { - int index = header.indexOf("="); - if (index > 0) { - String key = header.substring(0, index); - String value = header.substring(index + 1); - if (!StringUtils.isEmpty(key)) { - RpcContext.getServerAttachment().setAttachment(key.trim(), value.trim()); - } - } - } - } - } - - @Override - public void filter(ClientRequestContext requestContext) throws IOException { - int size = 0; - Map objectAttachments = new HashMap<>(RpcContext.getClientAttachment().getObjectAttachments()); - Invocation invocation = RpcContext.getServiceContext().getInvocation(); - //should merge attachments from invocation and RpcContext.getClientAttachment() - if(invocation != null && CollectionUtils.isNotEmptyMap(invocation.getObjectAttachments()) ){ - objectAttachments.putAll(invocation.getObjectAttachments()); - } - for (Map.Entry entry : objectAttachments.entrySet()) { - String key = entry.getKey(); - String value = (String) entry.getValue(); - if (illegalHttpHeaderKey(key) || illegalHttpHeaderValue(value)) { - throw new IllegalArgumentException("The attachments of " + RpcContext.class.getSimpleName() + " must not contain ',' or '=' when using rest protocol"); - } - - // TODO for now we don't consider the differences of encoding and server limit - if (value != null) { - size += value.getBytes(StandardCharsets.UTF_8).length; - } - if (size > MAX_HEADER_SIZE) { - throw new IllegalArgumentException("The attachments of " + RpcContext.class.getSimpleName() + " is too big"); - } - - String attachments = key + "=" + value; - requestContext.getHeaders().add(DUBBO_ATTACHMENT_HEADER, attachments); - } - } - - private boolean illegalHttpHeaderKey(String key) { - if (StringUtils.isNotEmpty(key)) { - return key.contains(CommonConstants.COMMA_SEPARATOR) || key.contains("="); - } - return false; - } - - private boolean illegalHttpHeaderValue(String value) { - if (StringUtils.isNotEmpty(value)) { - return value.contains(CommonConstants.COMMA_SEPARATOR); - } - return false; - } -} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapper.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapper.java index de6d2e45a6..a50a2dda5a 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapper.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapper.java @@ -17,25 +17,14 @@ package org.apache.dubbo.rpc.protocol.rest; import org.apache.dubbo.rpc.RpcException; -import org.apache.dubbo.rpc.protocol.rest.support.ContentType; +import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionHandler; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -public class RpcExceptionMapper implements ExceptionMapper { +public class RpcExceptionMapper implements ExceptionHandler { - @Override - public Response toResponse(RpcException e) { - if (e.getCause() instanceof ConstraintViolationException) { - return handleConstraintViolationException((ConstraintViolationException) e.getCause()); - } - // we may want to avoid exposing the dubbo exception details to certain clients - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Internal server error: " + e.getMessage()).type(ContentType.TEXT_PLAIN_UTF_8).build(); - } - - protected Response handleConstraintViolationException(ConstraintViolationException cve) { + protected Object handleConstraintViolationException(ConstraintViolationException cve) { ViolationReport report = new ViolationReport(); for (ConstraintViolation cv : cve.getConstraintViolations()) { report.addConstraintViolation(new RestConstraintViolation( @@ -43,6 +32,14 @@ public class RpcExceptionMapper implements ExceptionMapper { cv.getMessage(), cv.getInvalidValue() == null ? "null" : cv.getInvalidValue().toString())); } - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(report).type(ContentType.TEXT_XML_UTF_8).build(); + return report; + } + + @Override + public Object result(RpcException e) { + if (e.getCause() instanceof ConstraintViolationException) { + return handleConstraintViolationException((ConstraintViolationException) e.getCause()); + } + return "Internal server error: " + e.getMessage(); } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParserManager.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParserManager.java index 476f7aaed0..fb865bf417 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParserManager.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParserManager.java @@ -21,6 +21,8 @@ import org.apache.dubbo.metadata.rest.ArgInfo; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser; import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.ConsumerParseContext; +import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.BaseProviderParamParser; +import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.ProviderParseContext; import java.util.List; import java.util.Set; @@ -31,6 +33,9 @@ public class ParamParserManager { private static final Set consumerParamParsers = FrameworkModel.defaultModel().getExtensionLoader(BaseConsumerParamParser.class).getSupportedExtensionInstances(); + private static final Set providerParamParsers = + FrameworkModel.defaultModel().getExtensionLoader(BaseProviderParamParser.class).getSupportedExtensionInstances(); + /** * provider Design Description: *

@@ -41,11 +46,35 @@ public class ParamParserManager { *

* args=toArray(new Object[0]); */ - public void consumerParamParse(ConsumerParseContext parseContext) { + public static Object[] providerParamParse(ProviderParseContext parseContext) { - List args = parseContext.getArgs(); + List args = parseContext.getArgInfos(); for (int i = 0; i < args.size(); i++) { + for (ParamParser paramParser : providerParamParsers) { + + paramParser.parse(parseContext, args.get(i)); + } + } + return parseContext.getArgs().toArray(new Object[0]); + } + + + /** + * consumer Design Description: + *

+ * Object[] args=new Object[0]; + * List argsList=new ArrayList<>; + *

+ * setValueByIndex(int index,Object value); + *

+ * args=toArray(new Object[0]); + */ + public static void consumerParamParse(ConsumerParseContext parseContext) { + + List argInfos = parseContext.getArgInfos(); + + for (int i = 0; i < argInfos.size(); i++) { for (BaseConsumerParamParser paramParser : consumerParamParsers) { ArgInfo argInfoByIndex = parseContext.getArgInfoByIndex(i); diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionCreateContext.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionCreateContext.java index 73e0635cc4..584b831835 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionCreateContext.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionCreateContext.java @@ -25,8 +25,8 @@ import org.apache.dubbo.rpc.Invocation; public class HttpConnectionCreateContext { private RequestTemplate requestTemplate; - private HttpConnectionConfig connectionConfig; private RestMethodMetadata restMethodMetadata; + private ServiceRestMetadata serviceRestMetadata; private Invocation invocation; private URL url; @@ -38,21 +38,13 @@ public class HttpConnectionCreateContext { this.requestTemplate = requestTemplate; } - public void setConnectionConfig(HttpConnectionConfig connectionConfig) { - this.connectionConfig = connectionConfig; - } - public RequestTemplate getRequestTemplate() { return requestTemplate; } - public HttpConnectionConfig getConnectionConfig() { - return connectionConfig; - } - public ServiceRestMetadata getServiceRestMetadata() { - return restMethodMetadata.getServiceRestMetadata(); + return serviceRestMetadata; } public RestMethodMetadata getRestMethodMetadata() { @@ -78,4 +70,8 @@ public class HttpConnectionCreateContext { public void setUrl(URL url) { this.url = url; } + + public void setServiceRestMetadata(ServiceRestMetadata serviceRestMetadata) { + this.serviceRestMetadata = serviceRestMetadata; + } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionPreBuildIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionPreBuildIntercept.java index 3862e18314..a25466ed20 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionPreBuildIntercept.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionPreBuildIntercept.java @@ -19,6 +19,9 @@ package org.apache.dubbo.rpc.protocol.rest.annotation.consumer; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; +/** + * http request build intercept + */ @SPI(scope = ExtensionScope.FRAMEWORK) public interface HttpConnectionPreBuildIntercept { void intercept(HttpConnectionCreateContext connectionCreateContext); diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AddMustAttachmentIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AddMustAttachmentIntercept.java index 446cd4e6f3..c5e6248d5b 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AddMustAttachmentIntercept.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AddMustAttachmentIntercept.java @@ -20,11 +20,14 @@ package org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.metadata.rest.ServiceRestMetadata; import org.apache.dubbo.remoting.http.RequestTemplate; -import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionConfig; +import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum; import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext; import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept; import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; +/** + * add some must attachment + */ @Activate(value = RestConstant.ADD_MUST_ATTTACHMENT,order = 1) public class AddMustAttachmentIntercept implements HttpConnectionPreBuildIntercept { @@ -33,13 +36,12 @@ public class AddMustAttachmentIntercept implements HttpConnectionPreBuildInterce RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate(); ServiceRestMetadata serviceRestMetadata = connectionCreateContext.getServiceRestMetadata(); - HttpConnectionConfig connectionConfig = connectionCreateContext.getConnectionConfig(); - requestTemplate.addHeader(RestConstant.GROUP, serviceRestMetadata.getGroup()); - requestTemplate.addHeader(RestConstant.VERSION, serviceRestMetadata.getVersion()); - requestTemplate.addHeader(RestConstant.PATH, serviceRestMetadata.getServiceInterface()); - requestTemplate.addKeepAliveHeader(connectionConfig.getKeepAlive()); + requestTemplate.addHeader(RestHeaderEnum.GROUP.getHeader(), serviceRestMetadata.getGroup()); + requestTemplate.addHeader(RestHeaderEnum.VERSION.getHeader(), serviceRestMetadata.getVersion()); + requestTemplate.addHeader(RestHeaderEnum.PATH.getHeader(), serviceRestMetadata.getServiceInterface()); + requestTemplate.addHeader(RestHeaderEnum.TOKEN_KEY.getHeader(), connectionCreateContext.getUrl().getParameter(RestConstant.TOKEN_KEY)); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AttachmentIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AttachmentIntercept.java index 3903aa654c..d1e1126f5d 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AttachmentIntercept.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AttachmentIntercept.java @@ -17,54 +17,24 @@ package org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept; import org.apache.dubbo.common.extension.Activate; -import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.http.RequestTemplate; -import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext; import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept; import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; +import org.apache.dubbo.rpc.protocol.rest.util.HttpHeaderUtil; -import java.nio.charset.StandardCharsets; -import java.util.Map; -@Activate(value = RestConstant.RPCCONTEXT_INTERCEPT,order = 3) +/** + * add client rpc context to request geader + */ +@Activate(value = RestConstant.RPCCONTEXT_INTERCEPT, order = 3) public class AttachmentIntercept implements HttpConnectionPreBuildIntercept { @Override public void intercept(HttpConnectionCreateContext connectionCreateContext) { RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate(); - int size = 0; - for (Map.Entry entry : connectionCreateContext.getInvocation().getObjectAttachments().entrySet()) { - String key = entry.getKey(); - String value = String.valueOf(entry.getValue()); - if (illegalHttpHeaderKey(key) || illegalHttpHeaderValue(value)) { - throw new IllegalArgumentException("The attachments of " + RpcContext.class.getSimpleName() + " must not contain ',' or '=' when using rest protocol"); - } - // TODO for now we don't consider the differences of encoding and server limit - if (value != null) { - size += value.getBytes(StandardCharsets.UTF_8).length; - } - if (size > RestConstant.MAX_HEADER_SIZE) { - throw new IllegalArgumentException("The attachments of " + RpcContext.class.getSimpleName() + " is too big"); - } - - String attachments = key + "=" + value; - requestTemplate.addHeader(RestConstant.DUBBO_ATTACHMENT_HEADER, attachments); - } + HttpHeaderUtil.addRequestAttachments(requestTemplate, connectionCreateContext.getInvocation().getObjectAttachments()); } - private boolean illegalHttpHeaderKey(String key) { - if (StringUtils.isNotEmpty(key)) { - return key.contains(",") || key.contains("="); - } - return false; - } - - private boolean illegalHttpHeaderValue(String value) { - if (StringUtils.isNotEmpty(value)) { - return value.contains(","); - } - return false; - } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/ParamParseIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/ParamParseIntercept.java index 9babb8c605..951d19476b 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/ParamParseIntercept.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/ParamParseIntercept.java @@ -24,9 +24,11 @@ import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.Consum import java.util.Arrays; +/** + * resolve method args by args info + */ @Activate(value = "paramparse",order = 5) public class ParamParseIntercept implements HttpConnectionPreBuildIntercept { - private static final ParamParserManager paramParser = new ParamParserManager(); @Override public void intercept(HttpConnectionCreateContext connectionCreateContext) { @@ -34,6 +36,6 @@ public class ParamParseIntercept implements HttpConnectionPreBuildIntercept { ConsumerParseContext consumerParseContext = new ConsumerParseContext(connectionCreateContext.getRequestTemplate()); consumerParseContext.setArgInfos(connectionCreateContext.getRestMethodMetadata().getArgInfos()); consumerParseContext.setArgs(Arrays.asList(connectionCreateContext.getInvocation().getArguments())); - paramParser.consumerParamParse(consumerParseContext); + ParamParserManager.consumerParamParse(consumerParseContext); } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/PathVariableIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/PathVariableIntercept.java index 8329b086ad..9837d352b8 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/PathVariableIntercept.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/PathVariableIntercept.java @@ -29,6 +29,9 @@ import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; import java.util.Arrays; import java.util.List; +/** + * resolve method args from path + */ @Activate(value = RestConstant.PATH_INTERCEPT,order = 4) public class PathVariableIntercept implements HttpConnectionPreBuildIntercept { diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/RequestHeaderIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/RequestHeaderIntercept.java index e9ee687118..a2c1ce5abc 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/RequestHeaderIntercept.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/RequestHeaderIntercept.java @@ -20,6 +20,7 @@ package org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.metadata.rest.RestMethodMetadata; import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum; import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext; import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept; import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; @@ -27,7 +28,10 @@ import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; import java.util.Collection; import java.util.Set; -@Activate(value = RestConstant.REQUEST_HEADER_INTERCEPT,order = 2) +/** + * resolve method args from header + */ +@Activate(value = RestConstant.REQUEST_HEADER_INTERCEPT, order = 2) public class RequestHeaderIntercept implements HttpConnectionPreBuildIntercept { @Override @@ -37,16 +41,22 @@ public class RequestHeaderIntercept implements HttpConnectionPreBuildIntercept { RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate(); - Set consumes = restMethodMetadata.getRequest().getConsumes(); - requestTemplate.addHeaders(RestConstant.CONTENT_TYPE, consumes); + requestTemplate.addHeaders(RestHeaderEnum.CONTENT_TYPE.getHeader(), consumes); - Collection headers = requestTemplate.getHeaders(RestConstant.ACCEPT); - if (headers == null || headers.isEmpty()) { - requestTemplate.addHeader(RestConstant.ACCEPT, RestConstant.DEFAULT_ACCEPT); + Collection produces = restMethodMetadata.getRequest().getProduces(); + if (produces == null || produces.isEmpty()) { + requestTemplate.addHeader(RestHeaderEnum.ACCEPT.getHeader(), RestConstant.DEFAULT_ACCEPT); + } else { + requestTemplate.addHeader(RestHeaderEnum.ACCEPT.getHeader(), produces); } +// URL url = connectionCreateContext.getUrl(); + + +// requestTemplate.addKeepAliveHeader(url.getParameter(RestConstant.KEEP_ALIVE_TIMEOUT_PARAM,RestConstant.KEEP_ALIVE_TIMEOUT)); + } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/SerializeBodyIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/SerializeBodyIntercept.java index 6902cb987b..a29792be5b 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/SerializeBodyIntercept.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/SerializeBodyIntercept.java @@ -24,6 +24,7 @@ import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.metadata.rest.media.MediaType; import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext; import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept; import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; @@ -33,6 +34,9 @@ import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil; import java.io.ByteArrayOutputStream; import java.util.Collection; +/** + * for request body Serialize + */ @Activate(value = RestConstant.SERIALIZE_INTERCEPT, order = Integer.MAX_VALUE) public class SerializeBodyIntercept implements HttpConnectionPreBuildIntercept { @@ -50,13 +54,21 @@ public class SerializeBodyIntercept implements HttpConnectionPreBuildIntercept { try { Object unSerializedBody = requestTemplate.getUnSerializedBody(); URL url = connectionCreateContext.getUrl(); + // TODO pool ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); Collection headers = requestTemplate.getHeaders(RestConstant.CONTENT_TYPE); - MediaType mediaType = MediaTypeUtil.convertMediaType(headers.toArray(new String[0])); - HttpMessageCodecManager.httpMessageEncode(outputStream, unSerializedBody, url, mediaType); + MediaType mediaType = MediaTypeUtil.convertMediaType(requestTemplate.getBodyType(), headers.toArray(new String[0])); + + // add mediaType by targetClass serialize + if (headers.isEmpty() && mediaType != null && !mediaType.equals(MediaType.ALL_VALUE)) { + headers.add(mediaType.value); + } + HttpMessageCodecManager.httpMessageEncode(outputStream, unSerializedBody, url, mediaType, requestTemplate.getBodyType()); requestTemplate.serializeBody(outputStream.toByteArray()); + outputStream.close(); } catch (Exception e) { logger.error(LoggerCodeConstants.PROTOCOL_ERROR_DESERIALIZE, "", "", "Rest SerializeBodyIntercept serialize error: {}", e); + throw new RpcException(e); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/metadata/MetadataResolver.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/metadata/MetadataResolver.java index 69715715a1..f1521476a1 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/metadata/MetadataResolver.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/metadata/MetadataResolver.java @@ -18,13 +18,10 @@ package org.apache.dubbo.rpc.protocol.rest.annotation.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; -import org.apache.dubbo.metadata.ParameterTypesComparator; -import org.apache.dubbo.metadata.rest.RestMethodMetadata; import org.apache.dubbo.metadata.rest.ServiceRestMetadata; import org.apache.dubbo.metadata.rest.ServiceRestMetadataResolver; import org.apache.dubbo.rpc.protocol.rest.exception.CodeStyleNotSupportException; -import java.util.Map; public class MetadataResolver { private MetadataResolver() { @@ -34,18 +31,19 @@ public class MetadataResolver { * for consumer * * @param targetClass target service class - * @param url consumer url + * @param url consumer url * @return rest metadata * @throws CodeStyleNotSupportException not support type */ - public static Map> resolveConsumerServiceMetadata(Class targetClass, URL url) { + public static ServiceRestMetadata resolveConsumerServiceMetadata(Class targetClass, URL url, String contextPathFromUrl) { ExtensionLoader extensionLoader = url.getOrDefaultApplicationModel().getExtensionLoader(ServiceRestMetadataResolver.class); for (ServiceRestMetadataResolver serviceRestMetadataResolver : extensionLoader.getSupportedExtensionInstances()) { if (serviceRestMetadataResolver.supports(targetClass, true)) { ServiceRestMetadata serviceRestMetadata = new ServiceRestMetadata(url.getServiceInterface(), url.getVersion(), url.getGroup(), true); + serviceRestMetadata.setContextPathFromUrl(contextPathFromUrl); ServiceRestMetadata resolve = serviceRestMetadataResolver.resolve(targetClass, serviceRestMetadata); - return resolve.getMethodToServiceMap(); + return resolve; } } @@ -54,4 +52,19 @@ public class MetadataResolver { } + public static ServiceRestMetadata resolveProviderServiceMetadata(Class serviceImpl, URL url, String contextPathFromUrl) { + ExtensionLoader extensionLoader = url.getOrDefaultApplicationModel().getExtensionLoader(ServiceRestMetadataResolver.class); + + for (ServiceRestMetadataResolver serviceRestMetadataResolver : extensionLoader.getSupportedExtensionInstances()) { + boolean supports = serviceRestMetadataResolver.supports(serviceImpl); + if (supports) { + ServiceRestMetadata serviceRestMetadata = new ServiceRestMetadata(url.getServiceInterface(), url.getVersion(), url.getGroup(), false); + serviceRestMetadata.setContextPathFromUrl(contextPathFromUrl); + ServiceRestMetadata resolve = serviceRestMetadataResolver.resolve(serviceImpl, serviceRestMetadata); + return resolve; + } + } + throw new CodeStyleNotSupportException("service is:" + serviceImpl + ",just support rest or spring-web annotation"); + } + } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BodyConsumerParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BodyConsumerParamParser.java index 0e9e02a9af..0632ca3474 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BodyConsumerParamParser.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BodyConsumerParamParser.java @@ -33,7 +33,7 @@ public class BodyConsumerParamParser implements BaseConsumerParamParser { RequestTemplate requestTemplate = parseContext.getRequestTemplate(); - requestTemplate.body(args.get(argInfo.getIndex())); + requestTemplate.body(args.get(argInfo.getIndex()),argInfo.getParamType()); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/FormConsumerParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/FormConsumerParamParser.java index dba932b8de..2b2e04f1ba 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/FormConsumerParamParser.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/FormConsumerParamParser.java @@ -17,12 +17,21 @@ package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer; import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.metadata.rest.ArgInfo; import org.apache.dubbo.metadata.rest.ParamType; +import org.apache.dubbo.metadata.rest.media.MediaType; import org.apache.dubbo.remoting.http.RequestTemplate; -import org.apache.dubbo.rpc.protocol.rest.util.MultiValueCreator; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; +import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; @Activate("consumer-form") public class FormConsumerParamParser implements BaseConsumerParamParser { @@ -35,14 +44,34 @@ public class FormConsumerParamParser implements BaseConsumerParamParser { RequestTemplate requestTemplate = parseContext.getRequestTemplate(); Object value = args.get(argInfo.getIndex()); - Object unSerializedBody = requestTemplate.getUnSerializedBody(); - if (unSerializedBody == null) { - unSerializedBody = MultiValueCreator.createMultiValueMap(); + if (value == null) { + return; } - MultiValueCreator.add(unSerializedBody, argInfo.getAnnotationNameAttribute(), String.valueOf(value)); - requestTemplate.body(unSerializedBody); + Map> tmp = new HashMap<>(); + if (DataParseUtils.isTextType(value.getClass())) { + tmp.put(argInfo.getAnnotationNameAttribute(), Arrays.asList(String.valueOf(value))); + requestTemplate.body(tmp, Map.class); + } else if (value instanceof Map) { + requestTemplate.body(value, Map.class); + } else { + Set allFieldNames = ReflectUtils.getAllFieldNames(value.getClass()); + + allFieldNames.stream().forEach(entry -> { + + Object fieldValue = ReflectUtils.getFieldValue(value, entry); + tmp.put(String.valueOf(entry), Arrays.asList(String.valueOf(fieldValue))); + } + ); + + requestTemplate.body(tmp, Map.class); + } + + Collection headers = requestTemplate.getHeaders(RestConstant.CONTENT_TYPE); + if (CollectionUtils.isEmpty(headers)) { + requestTemplate.addHeader(RestConstant.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE.value); + } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/HeaderConsumerParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/HeaderConsumerParamParser.java index b53591e491..d99a01164d 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/HeaderConsumerParamParser.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/HeaderConsumerParamParser.java @@ -22,6 +22,7 @@ import org.apache.dubbo.metadata.rest.ParamType; import org.apache.dubbo.remoting.http.RequestTemplate; import java.util.List; +import java.util.Map; @Activate("consumer-header") public class HeaderConsumerParamParser implements BaseConsumerParamParser { @@ -31,7 +32,25 @@ public class HeaderConsumerParamParser implements BaseConsumerParamParser { RequestTemplate requestTemplate = parseContext.getRequestTemplate(); - requestTemplate.addHeader(argInfo.getParamName(), args.get(argInfo.getIndex())); + Object headerValue = args.get(argInfo.getIndex()); + + if (headerValue == null) { + return; + } + + + // Map + if (Map.class.isAssignableFrom(argInfo.getParamType())) { + Map headerValues = (Map) headerValue; + for (Object name : headerValues.keySet()) { + requestTemplate.addHeader(String.valueOf(name), headerValues.get(name)); + } + } else { + // others + requestTemplate.addHeader(argInfo.getAnnotationNameAttribute(), headerValue); + + } + } @Override diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ParameterConsumerParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ParameterConsumerParamParser.java index eb2746f1bb..11509a7965 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ParameterConsumerParamParser.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ParameterConsumerParamParser.java @@ -22,16 +22,34 @@ import org.apache.dubbo.metadata.rest.ParamType; import org.apache.dubbo.remoting.http.RequestTemplate; import java.util.List; +import java.util.Map; @Activate("consumer-parameter") -public class ParameterConsumerParamParser implements BaseConsumerParamParser{ +public class ParameterConsumerParamParser implements BaseConsumerParamParser { @Override public void parse(ConsumerParseContext parseContext, ArgInfo argInfo) { List args = parseContext.getArgs(); RequestTemplate requestTemplate = parseContext.getRequestTemplate(); - requestTemplate.addParam(argInfo.getParamName(), args.get(argInfo.getIndex())); + Object paramValue = args.get(argInfo.getIndex()); + + if (paramValue == null) { + return; + } + + + if (Map.class.isAssignableFrom(argInfo.getParamType())) { + Map paramValues = (Map) paramValue; + for (Object name : paramValues.keySet()) { + requestTemplate.addParam(String.valueOf(name), paramValues.get(name)); + } + } else { + requestTemplate.addParam(argInfo.getAnnotationNameAttribute(), paramValue); + + + } + } @Override diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/BaseProviderParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/BaseProviderParamParser.java new file mode 100644 index 0000000000..343efbbf62 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/BaseProviderParamParser.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider; + + +import org.apache.dubbo.common.extension.ExtensionScope; +import org.apache.dubbo.common.extension.SPI; +import org.apache.dubbo.rpc.protocol.rest.annotation.ParamParser; + +@SPI(scope = ExtensionScope.FRAMEWORK) +public interface BaseProviderParamParser extends ParamParser { + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/BodyProviderParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/BodyProviderParamParser.java new file mode 100644 index 0000000000..294de98fcb --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/BodyProviderParamParser.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.metadata.rest.ParamType; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; +import org.apache.dubbo.rpc.protocol.rest.exception.ParamParseException; +import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager; +import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade; +import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil; + + + +/** + * body param parse + */ +@Activate(value = RestConstant.PROVIDER_BODY_PARSE) +public class BodyProviderParamParser extends ProviderParamParser { + + @Override + protected void doParse(ProviderParseContext parseContext, ArgInfo argInfo) { + + RequestFacade request = parseContext.getRequestFacade(); + + try { + String contentType = parseContext.getRequestFacade().getHeader(RestHeaderEnum.CONTENT_TYPE.getHeader()); + MediaType mediaType = MediaTypeUtil.convertMediaType(argInfo.getParamType(), contentType); + Object param = HttpMessageCodecManager.httpMessageDecode(request.getInputStream(), argInfo.getParamType(), mediaType); + parseContext.setValueByIndex(argInfo.getIndex(), param); + } catch (Throwable e) { + throw new ParamParseException("dubbo rest protocol provider body param parser error: " + e.getMessage()); + } + } + + @Override + protected ParamType getParamType() { + return ParamType.PROVIDER_BODY; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/HeaderProviderParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/HeaderProviderParamParser.java new file mode 100644 index 0000000000..9b87792bd9 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/HeaderProviderParamParser.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; +import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade; +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.metadata.rest.ParamType; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * header param parse + */ +@Activate(value = RestConstant.PROVIDER_HEADER_PARSE) +public class HeaderProviderParamParser extends ProviderParamParser { + @Override + protected void doParse(ProviderParseContext parseContext, ArgInfo argInfo) { + + //TODO MAP convert + RequestFacade request = parseContext.getRequestFacade(); + if (Map.class.isAssignableFrom(argInfo.getParamType())) { + + Map headerMap = new LinkedHashMap<>(); + Enumeration headerNames = request.getHeaderNames(); + + while (headerNames.hasMoreElements()) { + String name = headerNames.nextElement(); + headerMap.put(name, request.getHeader(name)); + } + parseContext.setValueByIndex(argInfo.getIndex(), headerMap); + return; + + } + + + String header = request.getHeader(argInfo.getAnnotationNameAttribute()); + Object headerValue = paramTypeConvert(argInfo.getParamType(), header); + + + parseContext.setValueByIndex(argInfo.getIndex(), headerValue); + + } + + @Override + protected ParamType getParamType() { + return ParamType.HEADER; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ParamProviderParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ParamProviderParamParser.java new file mode 100644 index 0000000000..5505ba4b80 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ParamProviderParamParser.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider; + + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; +import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade; +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.metadata.rest.ParamType; + +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Http Parameter param parse + */ +@Activate(value = RestConstant.PROVIDER_PARAM_PARSE) +public class ParamProviderParamParser extends ProviderParamParser { + @Override + protected void doParse(ProviderParseContext parseContext, ArgInfo argInfo) { + + //TODO MAP convert + RequestFacade request = parseContext.getRequestFacade(); + + if (Map.class.isAssignableFrom(argInfo.getParamType())) { + + Map paramMap = new LinkedHashMap<>(); + Enumeration parameterNames = request.getParameterNames(); + + while (parameterNames.hasMoreElements()) { + String name = parameterNames.nextElement(); + paramMap.put(name, request.getParameter(name)); + } + parseContext.setValueByIndex(argInfo.getIndex(), paramMap); + return; + + } + + String param = request.getParameter(argInfo.getAnnotationNameAttribute()); + + Object paramValue = paramTypeConvert(argInfo.getParamType(), param); + parseContext.setValueByIndex(argInfo.getIndex(), paramValue); + } + + @Override + protected ParamType getParamType() { + return ParamType.PARAM; + } +} diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsDirectoryListener.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/PathProviderParamParser.java similarity index 50% rename from dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsDirectoryListener.java rename to dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/PathProviderParamParser.java index 27648f962d..4addb20a53 100644 --- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsDirectoryListener.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/PathProviderParamParser.java @@ -14,30 +14,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider; -package org.apache.dubbo.metrics.registry.event; -import org.apache.dubbo.metrics.event.MetricsEvent; -import org.apache.dubbo.metrics.listener.MetricsListener; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.metadata.rest.ParamType; -public class MetricsDirectoryListener implements MetricsListener { +/** + * path param parse + */ +@Activate(value = RestConstant.PROVIDER_PATH_PARSE) +public class PathProviderParamParser extends ProviderParamParser { @Override - public boolean isSupport(MetricsEvent event) { - return event instanceof RegistryEvent.MetricsDirectoryEvent; + protected void doParse(ProviderParseContext parseContext, ArgInfo argInfo) { + + String pathVariable = parseContext.getPathVariable(argInfo.getUrlSplitIndex()); + + Object pathVariableValue = paramTypeConvert(argInfo.getParamType(), pathVariable); + + parseContext.setValueByIndex(argInfo.getIndex(), pathVariableValue); + } @Override - public void onEvent(RegistryEvent.MetricsDirectoryEvent event) { - if (!event.isAvailable()) { - return; - } - if (event.getType().isIncrement()) { - event.getCollector().increment(event.getSource().getApplicationName(), event.getType()); - } else { - event.getCollector().setNum(event.getType(), event.getSource().getApplicationName(), event.getSize()); - } + protected ParamType getParamType() { + return ParamType.PATH; } - - } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ProviderParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ProviderParamParser.java new file mode 100644 index 0000000000..12606b0cf5 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ProviderParamParser.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider; + + +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.metadata.rest.ParamType; +import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils; + +public abstract class ProviderParamParser implements BaseProviderParamParser { + + public void parse(ProviderParseContext parseContext, ArgInfo argInfo) { + + if (!matchParseType(argInfo.getParamAnnotationType())) { + return; + } + + doParse(parseContext, argInfo); + } + + protected abstract void doParse(ProviderParseContext parseContext, ArgInfo argInfo); + + public boolean matchParseType(Class paramAnno) { + + ParamType paramAnnotType = getParamType(); + return paramAnnotType.supportAnno(paramAnno); + } + + protected abstract ParamType getParamType(); + + protected Object paramTypeConvert(Class targetType, String value) { + + + return DataParseUtils.stringTypeConvert(targetType, value); + + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ProviderParseContext.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ProviderParseContext.java new file mode 100644 index 0000000000..0e2be572a6 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ProviderParseContext.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider; + + +import org.apache.dubbo.rpc.protocol.rest.annotation.BaseParseContext; +import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade; + + +public class ProviderParseContext extends BaseParseContext { + + + private RequestFacade requestFacade; + private Object response; + private Object request; + + + public ProviderParseContext(RequestFacade request) { + this.requestFacade = request; + } + + public RequestFacade getRequestFacade() { + return requestFacade; + } + + public void setValueByIndex(int index, Object value) { + + this.args.set(index, value); + } + + public Object getResponse() { + return response; + } + + public void setResponse(Object response) { + this.response = response; + } + + public Object getRequest() { + return request; + } + + public void setRequest(Object request) { + this.request = request; + } + + public String getPathVariable(int urlSplitIndex) { + + String[] split = getRequestFacade().getRequestURI().split("/"); + + return split[urlSplitIndex]; + + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/constans/RestConstant.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/constans/RestConstant.java index 8fe65f03a1..5f429856da 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/constans/RestConstant.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/constans/RestConstant.java @@ -17,44 +17,51 @@ package org.apache.dubbo.rpc.protocol.rest.constans; import org.apache.dubbo.common.constants.CommonConstants; -import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.rpc.Constants; public interface RestConstant { - String INTERFACE = CommonConstants.INTERFACE_KEY; - String METHOD = CommonConstants.METHOD_KEY; - String PARAMETER_TYPES_DESC = CommonConstants.GENERIC_PARAMETER_DESC; String VERSION = CommonConstants.VERSION_KEY; String GROUP = CommonConstants.GROUP_KEY; String PATH = CommonConstants.PATH_KEY; - String HOST = CommonConstants.HOST_KEY; + String TOKEN_KEY = Constants.TOKEN_KEY; String LOCAL_ADDR = "LOCAL_ADDR"; String REMOTE_ADDR = "REMOTE_ADDR"; String LOCAL_PORT = "LOCAL_PORT"; String REMOTE_PORT = "REMOTE_PORT"; - String SERIALIZATION_KEY = Constants.SERIALIZATION_KEY; String PROVIDER_BODY_PARSE = "body"; String PROVIDER_PARAM_PARSE = "param"; String PROVIDER_HEADER_PARSE = "header"; String PROVIDER_PATH_PARSE = "path"; - String PROVIDER_REQUEST_PARSE = "reuqest"; - String DUBBO_ATTACHMENT_HEADER = "Dubbo-Attachments"; - int MAX_HEADER_SIZE = 8 * 1024; String ADD_MUST_ATTTACHMENT = "must-intercept"; String RPCCONTEXT_INTERCEPT = "rpc-context"; String SERIALIZE_INTERCEPT = "serialize"; - String CONTENT_TYPE_INTERCEPT = "content-type"; String PATH_SEPARATOR = "/"; - String REQUEST_PARAM_INTERCEPT = "param"; String REQUEST_HEADER_INTERCEPT = "header"; String PATH_INTERCEPT = "path"; String KEEP_ALIVE_HEADER = "Keep-Alive"; String CONNECTION = "Connection"; - String KEEP_ALIVE = "keep-alive"; String CONTENT_TYPE = "Content-Type"; - String APPLICATION_JSON_VALUE = "application/json"; - String APPLICATION_FORM_URLENCODED_VALUE = "application/x-www-form-urlencoded"; String TEXT_PLAIN = "text/plain"; String ACCEPT = "Accept"; String DEFAULT_ACCEPT = "*/*"; + String REST_HEADER_PREFIX = "#rest#"; + + + // http + String MAX_INITIAL_LINE_LENGTH_PARAM = "max.initial.line.length"; + String MAX_HEADER_SIZE_PARAM = "max.header.size"; + String MAX_CHUNK_SIZE_PARAM = "max.chunk.size"; + String MAX_REQUEST_SIZE_PARAM = "max.request.size"; + String IDLE_TIMEOUT_PARAM = "idle.timeout"; + String KEEP_ALIVE_TIMEOUT_PARAM = "keep.alive.timeout"; + + int MAX_REQUEST_SIZE = 1024 * 1024 * 10; + int MAX_INITIAL_LINE_LENGTH = 4096; + int MAX_HEADER_SIZE = 8192; + int MAX_CHUNK_SIZE = 8192; + int IDLE_TIMEOUT = -1; + int KEEP_ALIVE_TIMEOUT = 60; + + } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/CodeStyleNotSupportException.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/CodeStyleNotSupportException.java index db68cf1ac7..d2c6c5849d 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/CodeStyleNotSupportException.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/CodeStyleNotSupportException.java @@ -16,7 +16,10 @@ */ package org.apache.dubbo.rpc.protocol.rest.exception; -public class CodeStyleNotSupportException extends RuntimeException{ +/** + * only support spring mvc & jaxrs annotation + */ +public class CodeStyleNotSupportException extends RestException{ public CodeStyleNotSupportException(String message) { super(message); diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/TimeCounter.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/DoublePathCheckException.java similarity index 76% rename from dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/TimeCounter.java rename to dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/DoublePathCheckException.java index 780684cf1e..450f7a2da7 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/TimeCounter.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/DoublePathCheckException.java @@ -14,15 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.apache.dubbo.metrics.event; - -import org.apache.dubbo.metrics.model.TimePair; +package org.apache.dubbo.rpc.protocol.rest.exception; /** - * Mark certain types of events, allow automatic recording of start and end times, and provide time pairs + * path mapper contains current path will throw */ -public interface TimeCounter { +public class DoublePathCheckException extends RuntimeException { - TimePair getTimePair(); + public DoublePathCheckException(String message) { + super(message); + } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/MediaTypeUnSupportException.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/MediaTypeUnSupportException.java new file mode 100644 index 0000000000..7c3d922e44 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/MediaTypeUnSupportException.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.exception; + +public class MediaTypeUnSupportException extends RestException{ + public MediaTypeUnSupportException(String message) { + super(message); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/HttpClientException.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/ParamParseException.java similarity index 83% rename from dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/HttpClientException.java rename to dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/ParamParseException.java index 709e12af1f..b0cd3d7507 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/HttpClientException.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/ParamParseException.java @@ -16,9 +16,9 @@ */ package org.apache.dubbo.rpc.protocol.rest.exception; -public class HttpClientException extends RuntimeException { +public class ParamParseException extends RestException { - public HttpClientException(String message) { - super("dubbo http rest protocol param error :"+message); + public ParamParseException(String message) { + super(message); } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/PathNoFoundException.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/PathNoFoundException.java new file mode 100644 index 0000000000..a8b2751fb3 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/PathNoFoundException.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.exception; + +/** + * response code : 404 path no found exception + */ +public class PathNoFoundException extends RestException{ + + public PathNoFoundException(String message) { + super(message); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RemoteServerInternalException.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RemoteServerInternalException.java index 1d1219fdbf..a552562dee 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RemoteServerInternalException.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RemoteServerInternalException.java @@ -16,7 +16,10 @@ */ package org.apache.dubbo.rpc.protocol.rest.exception; -public class RemoteServerInternalException extends RuntimeException { +/** + * response status code : 500 + */ +public class RemoteServerInternalException extends RestException { public RemoteServerInternalException(String message) { super("dubbo http rest protocol remote error :"+message); diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RestException.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RestException.java new file mode 100644 index 0000000000..9956eb87f7 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RestException.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.exception; + +/** + * rest exception super + */ +public class RestException extends RuntimeException { + + public RestException(String message) { + super(message); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/UnSupportContentTypeException.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/UnSupportContentTypeException.java index f895f6ec44..d4ad67b3b2 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/UnSupportContentTypeException.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/UnSupportContentTypeException.java @@ -18,7 +18,7 @@ package org.apache.dubbo.rpc.protocol.rest.exception; import org.apache.dubbo.metadata.rest.media.MediaType; -public class UnSupportContentTypeException extends RuntimeException { +public class UnSupportContentTypeException extends MediaTypeUnSupportException { public UnSupportContentTypeException(String message) { diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionHandler.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionHandler.java new file mode 100644 index 0000000000..792ee5d315 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionHandler.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.rpc.protocol.rest.exception.mapper; + + +public interface ExceptionHandler { + + Object result(E exception); + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionMapper.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionMapper.java new file mode 100644 index 0000000000..cbfc1ad1e5 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionMapper.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.rpc.protocol.rest.exception.mapper; + +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.rpc.protocol.rest.util.ReflectUtils; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class ExceptionMapper { + private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); + + + // TODO static or instance ? think about influence between difference url exception + private final Map, ExceptionHandler> exceptionHandlerMap = new ConcurrentHashMap<>(); + + public Object exceptionToResult(Object throwable) { + if (!hasExceptionMapper(throwable)) { + return throwable; + } + + return exceptionHandlerMap.get(throwable.getClass()).result((Throwable) throwable); + } + + public boolean hasExceptionMapper(Object throwable) { + if (throwable == null) { + return false; + } + return exceptionHandlerMap.containsKey(throwable.getClass()); + } + + + public void registerMapper(Class exceptionHandler) { + + + try { + if (!ExceptionHandler.class.isAssignableFrom(exceptionHandler)) { + return; + } + // resolve Java_Zulu_jdk/17.0.6-10/x64 param is not throwable + List methods = ReflectUtils.getMethodByNameList(exceptionHandler, "result"); + + + Set> exceptions = new HashSet<>(); + + for (Method method : methods) { + Class parameterType = method.getParameterTypes()[0]; + + // param type isAssignableFrom throwable + if (!Throwable.class.isAssignableFrom(parameterType)) { + continue; + } + + exceptions.add(parameterType); + } + + ArrayList> classes = new ArrayList<>(exceptions); + + // if size==1 so ,exception handler for Throwable + if (classes.size() != 1) { + // else remove throwable + exceptions.remove(Throwable.class); + } + + List> constructors = ReflectUtils.getConstructList(exceptionHandler); + + if (constructors.isEmpty()) { + throw new RuntimeException("dubbo rest exception mapper register mapper need exception handler exist no construct declare, current class is: " + exceptionHandler); + } + + // if exceptionHandler is inner class , no arg construct don`t appear , so newInstance don`t use noArgConstruct + Object handler = constructors.get(0).newInstance(new Object[constructors.get(0).getParameterCount()]); + + for (Class exception : exceptions) { + exceptionHandlerMap.put(exception, (ExceptionHandler) handler); + } + + } catch (Exception e) { + throw new RuntimeException("dubbo rest protocol exception mapper register error ", e); + } + + + } + + public void registerMapper(String exceptionMapper) { + try { + registerMapper(ReflectUtils.findClass(exceptionMapper)); + } catch (Exception e) { + logger.warn("", e.getMessage(), "", "dubbo rest protocol exception mapper register error ,and current exception mapper is :" + exceptionMapper); + } + + } + + + public void unRegisterMapper(Class exception) { + exceptionHandlerMap.remove(exception); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/handler/NettyHttpHandler.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/handler/NettyHttpHandler.java new file mode 100644 index 0000000000..a3243685ab --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/handler/NettyHttpHandler.java @@ -0,0 +1,210 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.handler; + +import io.netty.handler.codec.http.FullHttpRequest; +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.remoting.http.HttpHandler; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.protocol.rest.PathAndInvokerMapper; +import org.apache.dubbo.rpc.protocol.rest.RestRPCInvocationUtil; +import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum; +import org.apache.dubbo.rpc.protocol.rest.exception.MediaTypeUnSupportException; +import org.apache.dubbo.rpc.protocol.rest.exception.ParamParseException; +import org.apache.dubbo.rpc.protocol.rest.exception.PathNoFoundException; +import org.apache.dubbo.rpc.protocol.rest.exception.UnSupportContentTypeException; +import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionMapper; +import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager; +import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse; +import org.apache.dubbo.rpc.protocol.rest.pair.InvokerAndRestMethodMetadataPair; +import org.apache.dubbo.rpc.protocol.rest.pair.MessageCodecResultPair; +import org.apache.dubbo.rpc.protocol.rest.request.NettyRequestFacade; +import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade; +import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil; + +import java.io.IOException; + +/** + * netty http request handler + */ +public class NettyHttpHandler implements HttpHandler { + private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); + private final PathAndInvokerMapper pathAndInvokerMapper; + private final ExceptionMapper exceptionMapper; + + + public NettyHttpHandler(PathAndInvokerMapper pathAndInvokerMapper, ExceptionMapper exceptionMapper) { + this.pathAndInvokerMapper = pathAndInvokerMapper; + this.exceptionMapper = exceptionMapper; + } + + @Override + public void handle(NettyRequestFacade requestFacade, NettyHttpResponse nettyHttpResponse) throws IOException { + + // set remote address + RpcContext.getServiceContext().setRemoteAddress(requestFacade.getRemoteAddr(), requestFacade.getRemotePort()); + + // set local address + RpcContext.getServiceContext().setLocalAddress(requestFacade.getLocalAddr(), requestFacade.getLocalPort()); + + // set request + RpcContext.getServiceContext().setRequest(requestFacade); + + // set response + RpcContext.getServiceContext().setResponse(nettyHttpResponse); + // TODO add request filter chain + + FullHttpRequest nettyHttpRequest = requestFacade.getRequest(); + + try { + doHandler(nettyHttpRequest, nettyHttpResponse, requestFacade); + } catch (PathNoFoundException pathNoFoundException) { + logger.error("", pathNoFoundException.getMessage(), "", "dubbo rest protocol provider path no found ,raw request is :" + nettyHttpRequest, pathNoFoundException); + nettyHttpResponse.sendError(404, pathNoFoundException.getMessage()); + } catch (ParamParseException paramParseException) { + logger.error("", paramParseException.getMessage(), "", "dubbo rest protocol provider param parse error ,and raw request is :" + nettyHttpRequest, paramParseException); + nettyHttpResponse.sendError(400, paramParseException.getMessage()); + } catch (MediaTypeUnSupportException contentTypeException) { + logger.error("", contentTypeException.getMessage(), "", "dubbo rest protocol provider content-type un support" + nettyHttpRequest, contentTypeException); + nettyHttpResponse.sendError(415, contentTypeException.getMessage()); + } catch (Throwable throwable) { + logger.error("", throwable.getMessage(), "", "dubbo rest protocol provider error ,and raw request is " + nettyHttpRequest, throwable); + nettyHttpResponse.sendError(500, "dubbo rest invoke Internal error, message is " + throwable.getMessage() + + " , stacktrace is: " + stackTraceToString(throwable)); + } + + + } + + private void doHandler(FullHttpRequest nettyHttpRequest, NettyHttpResponse nettyHttpResponse, RequestFacade request) throws Exception { + // acquire metadata by request + InvokerAndRestMethodMetadataPair restMethodMetadataPair = RestRPCInvocationUtil.getRestMethodMetadata(request, pathAndInvokerMapper); + + Invoker invoker = restMethodMetadataPair.getInvoker(); + + RestMethodMetadata restMethodMetadata = restMethodMetadataPair.getRestMethodMetadata(); + + // content-type support judge,throw unSupportException + acceptSupportJudge(request, restMethodMetadata.getReflectMethod().getReturnType()); + + // build RpcInvocation + RpcInvocation rpcInvocation = RestRPCInvocationUtil.createBaseRpcInvocation(request, restMethodMetadata); + + // parse method real args + RestRPCInvocationUtil.parseMethodArgs(rpcInvocation, request, nettyHttpRequest, nettyHttpResponse, restMethodMetadata); + + // execute business method invoke + Result result = invoker.invoke(rpcInvocation); + + if (result.hasException()) { + Throwable exception = result.getException(); + logger.error("", exception.getMessage(), "", "dubbo rest protocol provider Invoker invoke error", exception); + + if (exceptionMapper.hasExceptionMapper(exception)) { + writeResult(nettyHttpResponse, request, invoker, exceptionMapper.exceptionToResult(result.getException()), rpcInvocation.getReturnType()); + nettyHttpResponse.setStatus(200); + } else { + nettyHttpResponse.sendError(500, + "\n dubbo rest business exception, error cause is: " + + result.getException().getCause() + + "\n message is: " + result.getException().getMessage() + + "\n stacktrace is: " + stackTraceToString(exception)); + } + } else { + Object value = result.getValue(); + writeResult(nettyHttpResponse, request, invoker, value, rpcInvocation.getReturnType()); + nettyHttpResponse.setStatus(200); + } + } + + + /** + * write return value by accept + * + * @param nettyHttpResponse + * @param request + * @param invoker + * @param value + * @param returnType + * @throws Exception + */ + private void writeResult(NettyHttpResponse nettyHttpResponse, RequestFacade request, Invoker invoker, Object value, Class returnType) throws Exception { + MediaType mediaType = getAcceptMediaType(request,returnType); + + MessageCodecResultPair booleanMediaTypePair = HttpMessageCodecManager.httpMessageEncode(nettyHttpResponse.getOutputStream(), value, invoker.getUrl(), mediaType, returnType); + + nettyHttpResponse.addOutputHeaders(RestHeaderEnum.CONTENT_TYPE.getHeader(), booleanMediaTypePair.getMediaType().value); + } + + /** + * return first match , if any multiple content-type + * + * @param request + * @return + */ + private MediaType getAcceptMediaType(RequestFacade request,Class returnType) { + String accept = request.getHeader(RestHeaderEnum.ACCEPT.getHeader()); + MediaType mediaType = MediaTypeUtil.convertMediaType(returnType, accept); + return mediaType; + } + + /** + * accept can not support will throw UnSupportAcceptException + * + * @param requestFacade + */ + private void acceptSupportJudge(RequestFacade requestFacade, Class returnType) { + try { + // media type judge + getAcceptMediaType(requestFacade,returnType); + } catch (UnSupportContentTypeException e) { + // return type judge + MediaType mediaType = HttpMessageCodecManager.typeSupport(returnType); + + String accept = requestFacade.getHeader(RestHeaderEnum.ACCEPT.getHeader()); + if (mediaType == null || accept == null) { + throw e; + } + + + if (!accept.contains(mediaType.value)) { + + throw e; + } + + } + } + + + public static String stackTraceToString(Throwable throwable) { + StackTraceElement[] stackTrace = throwable.getStackTrace(); + + StringBuilder stringBuilder = new StringBuilder("\n"); + for (StackTraceElement traceElement : stackTrace) { + stringBuilder.append("\tat " + traceElement).append("\n"); + } + + return stringBuilder.toString(); + } + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodec.java index 1b6666b8f2..f38ee24a57 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodec.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodec.java @@ -21,10 +21,30 @@ import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.metadata.rest.media.MediaType; +/** + * for http body codec + * @param + * @param + */ @SPI(scope = ExtensionScope.FRAMEWORK) public interface HttpMessageCodec extends HttpMessageDecode, HttpMessageEncode { - boolean contentTypeSupport(MediaType mediaType, Class targetType); + /** + * content-type support judge + * @param mediaType + * @param targetType + * @return + */ + boolean contentTypeSupport(MediaType mediaType, Class targetType); + + /** + * class type support judge + * @param targetType + * @return + */ + boolean typeSupport(Class targetType); + + MediaType contentType(); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodecManager.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodecManager.java index 019927ca36..27b6ef57e7 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodecManager.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodecManager.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.metadata.rest.media.MediaType; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.rest.exception.UnSupportContentTypeException; +import org.apache.dubbo.rpc.protocol.rest.pair.MessageCodecResultPair; import java.io.OutputStream; import java.util.Set; @@ -29,23 +30,65 @@ public class HttpMessageCodecManager { FrameworkModel.defaultModel().getExtensionLoader(HttpMessageCodec.class).getSupportedExtensionInstances(); - public static Object httpMessageDecode(byte[] body, Class type, MediaType mediaType) throws Exception { + public static Object httpMessageDecode(byte[] body, Class type, MediaType mediaType) throws Exception { + if (body == null || body.length == 0) { + return null; + } + for (HttpMessageCodec httpMessageCodec : httpMessageCodecs) { - if (httpMessageCodec.contentTypeSupport(mediaType, type)) { + if (httpMessageCodec.contentTypeSupport(mediaType, type) || typeJudge(mediaType, type, httpMessageCodec)) { return httpMessageCodec.decode(body, type); } } throw new UnSupportContentTypeException("UnSupport content-type :" + mediaType.value); } - public static void httpMessageEncode(OutputStream outputStream, Object unSerializedBody, URL url, MediaType mediaType) throws Exception { - for (HttpMessageCodec httpMessageCodec : httpMessageCodecs) { - if (httpMessageCodec.contentTypeSupport(mediaType, unSerializedBody.getClass())) { - httpMessageCodec.encode(outputStream, unSerializedBody, url); - return; + public static MessageCodecResultPair httpMessageEncode(OutputStream outputStream, Object unSerializedBody, URL url, MediaType mediaType, Class bodyType) throws Exception { + + + if (unSerializedBody == null) { + for (HttpMessageCodec httpMessageCodec : httpMessageCodecs) { + if (httpMessageCodec.contentTypeSupport(mediaType, bodyType) || typeJudge(mediaType, bodyType, httpMessageCodec)) { + return MessageCodecResultPair.pair(false, httpMessageCodec.contentType()); + } } } + + for (HttpMessageCodec httpMessageCodec : httpMessageCodecs) { + if (httpMessageCodec.contentTypeSupport(mediaType, bodyType) || typeJudge(mediaType, bodyType, httpMessageCodec)) { + httpMessageCodec.encode(outputStream, unSerializedBody, url); + return MessageCodecResultPair.pair(true, httpMessageCodec.contentType()); + } + } + + throw new UnSupportContentTypeException("UnSupport content-type :" + mediaType.value); } + /** + * if content-type is null or all ,will judge media type by class type + * + * @param mediaType + * @param bodyType + * @param httpMessageCodec + * @return + */ + private static boolean typeJudge(MediaType mediaType, Class bodyType, HttpMessageCodec httpMessageCodec) { + return (MediaType.ALL_VALUE.equals(mediaType) || mediaType == null) + && bodyType != null && httpMessageCodec.typeSupport(bodyType); + } + + public static MediaType typeSupport(Class type) { + for (HttpMessageCodec httpMessageCodec : httpMessageCodecs) { + + if (httpMessageCodec.typeSupport(type)) { + return httpMessageCodec.contentType(); + } + + } + + return null; + } + + } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageDecode.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageDecode.java index 12ae381685..cf3b639b33 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageDecode.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageDecode.java @@ -19,6 +19,6 @@ package org.apache.dubbo.rpc.protocol.rest.message; public interface HttpMessageDecode{ - Object decode(InputStream body, Class targetType) throws Exception; + Object decode(InputStream body, Class targetType) throws Exception; } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/MediaTypeMatcher.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/MediaTypeMatcher.java index 2863f25893..6db52df861 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/MediaTypeMatcher.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/MediaTypeMatcher.java @@ -52,7 +52,6 @@ public enum MediaTypeMatcher { private static List getDefaultList() { List defaultList = new ArrayList<>(); - defaultList.add(MediaType.ALL_VALUE); return defaultList; } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ByteArrayCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ByteArrayCodec.java index 4210e23569..85dea16e00 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ByteArrayCodec.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ByteArrayCodec.java @@ -23,20 +23,33 @@ import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec; import java.io.OutputStream; +/** + * body type is byte array + */ @Activate("byteArray") public class ByteArrayCodec implements HttpMessageCodec { @Override - public Object decode(byte[] body, Class targetType) throws Exception { + public Object decode(byte[] body, Class targetType) throws Exception { return body; } @Override - public boolean contentTypeSupport(MediaType mediaType, Class targetType) { + public boolean contentTypeSupport(MediaType mediaType, Class targetType) { return byte[].class.equals(targetType); } + @Override + public boolean typeSupport(Class targetType) { + return byte[].class.equals(targetType); + } + + @Override + public MediaType contentType() { + return MediaType.OCTET_STREAM; + } + @Override public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception { diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/JsonCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/JsonCodec.java index 095c33bdda..7d494b53b2 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/JsonCodec.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/JsonCodec.java @@ -29,6 +29,9 @@ import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.Set; +/** + * body is json + */ @Activate("json") public class JsonCodec implements HttpMessageCodec { private static final Set unSupportClasses = new HashSet<>(); @@ -37,21 +40,32 @@ public class JsonCodec implements HttpMessageCodec { unSupportClasses.add(byte[].class); unSupportClasses.add(String.class); + } @Override - public Object decode(byte[] body, Class targetType) throws Exception { + public Object decode(byte[] body, Class targetType) throws Exception { return DataParseUtils.jsonConvert(targetType, body); } @Override - public boolean contentTypeSupport(MediaType mediaType, Class targetType) { + public boolean contentTypeSupport(MediaType mediaType, Class targetType) { return MediaTypeMatcher.APPLICATION_JSON.mediaSupport(mediaType) && !unSupportClasses.contains(targetType); } + @Override + public boolean typeSupport(Class targetType) { + return !unSupportClasses.contains(targetType) && !DataParseUtils.isTextType(targetType); + } + + @Override + public MediaType contentType() { + return MediaType.APPLICATION_JSON_VALUE; + } + @Override public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception { - outputStream.write(JsonUtils.getJson().toJson(unSerializedBody).getBytes(StandardCharsets.UTF_8)); + outputStream.write(JsonUtils.toJson(unSerializedBody).getBytes(StandardCharsets.UTF_8)); } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/MultiValueCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/MultiValueCodec.java index 97fbb5bedb..b41955e8e9 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/MultiValueCodec.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/MultiValueCodec.java @@ -18,27 +18,82 @@ package org.apache.dubbo.rpc.protocol.rest.message.codec; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.metadata.rest.media.MediaType; import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec; import org.apache.dubbo.rpc.protocol.rest.message.MediaTypeMatcher; import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils; import java.io.OutputStream; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import java.util.Set; +/** + * body is form + */ @Activate("multiValue") -public class MultiValueCodec implements HttpMessageCodec { +public class MultiValueCodec implements HttpMessageCodec { @Override - public Object decode(byte[] body, Class targetType) throws Exception { + public Object decode(byte[] body, Class targetType) throws Exception { // TODO java bean get set convert - return DataParseUtils.multipartFormConvert(body); + Object map = DataParseUtils.multipartFormConvert(body,targetType); + Map valuesMap = (Map) map; + if (Map.class.isAssignableFrom(targetType)) { + return map; + } else if (DataParseUtils.isTextType(targetType)) { + + // only fetch first + Set set = valuesMap.keySet(); + ArrayList arrayList = new ArrayList<>(set); + Object key = arrayList.get(0); + Object value = valuesMap.get(key); + if (value == null) { + return null; + } + return DataParseUtils.stringTypeConvert(targetType, String.valueOf(((List) value).get(0))); + + + } else { + + + Map beanPropertyFields = ReflectUtils.getBeanPropertyFields(targetType); + + Object emptyObject = ReflectUtils.getEmptyObject(targetType); + + beanPropertyFields.entrySet().stream().forEach(entry -> { + try { + List values = (List) valuesMap.get(entry.getKey()); + String value = values == null ? null : String.valueOf(values.get(0)); + entry.getValue().set(emptyObject, DataParseUtils.stringTypeConvert(entry.getValue().getType(), value)); + } catch (IllegalAccessException e) { + + } + }); + + return emptyObject; + } + + } + + + @Override + public boolean contentTypeSupport(MediaType mediaType, Class targetType) { + return MediaTypeMatcher.MULTI_VALUE.mediaSupport(mediaType); } @Override - public boolean contentTypeSupport(MediaType mediaType,Class targetType) { - return MediaTypeMatcher.MULTI_VALUE.mediaSupport(mediaType); + public boolean typeSupport(Class targetType) { + return false; + } + + @Override + public MediaType contentType() { + return MediaType.APPLICATION_FORM_URLENCODED_VALUE; } @Override diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/StringCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/StringCodec.java index 0deffe32b1..d30b2e5a11 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/StringCodec.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/StringCodec.java @@ -24,20 +24,36 @@ import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +/** + * body is string + */ @Activate("string") public class StringCodec implements HttpMessageCodec { @Override - public Object decode(byte[] body, Class targetType) throws Exception { + public Object decode(byte[] body, Class targetType) throws Exception { + if (body == null || body.length == 0) { + return null; + } return new String(body); } @Override - public boolean contentTypeSupport(MediaType mediaType,Class targetType) { + public boolean contentTypeSupport(MediaType mediaType,Class targetType) { return String.class.equals(targetType); } + @Override + public boolean typeSupport(Class targetType) { + return String.class.equals(targetType); + } + + @Override + public MediaType contentType() { + return MediaType.TEXT_PLAIN; + } + @Override public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception { diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/TextCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/TextCodec.java index c4722d01b3..63c06876f8 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/TextCodec.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/TextCodec.java @@ -26,20 +26,33 @@ import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +/** + * content-type is text/html + */ @Activate("text") public class TextCodec implements HttpMessageCodec { @Override - public Object decode(byte[] body, Class targetType) throws Exception { + public Object decode(byte[] body, Class targetType) throws Exception { return DataParseUtils.stringTypeConvert(targetType, new String(body, StandardCharsets.UTF_8)); } @Override - public boolean contentTypeSupport(MediaType mediaType, Class targetType) { + public boolean contentTypeSupport(MediaType mediaType, Class targetType) { return MediaTypeMatcher.TEXT_PLAIN.mediaSupport(mediaType); } + @Override + public boolean typeSupport(Class targetType) { + return DataParseUtils.isTextType(targetType); + } + + @Override + public MediaType contentType() { + return MediaType.TEXT_PLAIN; + } + @Override public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception { DataParseUtils.writeTextContent(unSerializedBody, outputStream); diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/XMLCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/XMLCodec.java index 766ea8064b..d58fd9e8de 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/XMLCodec.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/XMLCodec.java @@ -33,12 +33,15 @@ import javax.xml.transform.sax.SAXSource; import java.io.OutputStream; import java.io.StringReader; +/** + * body content-type is xml + */ @Activate("xml") public class XMLCodec implements HttpMessageCodec { @Override - public Object decode(byte[] body, Class targetType) throws Exception { + public Object decode(byte[] body, Class targetType) throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); @@ -56,16 +59,25 @@ public class XMLCodec implements HttpMessageCodec { } @Override - public boolean contentTypeSupport(MediaType mediaType, Class targetType) { + public boolean contentTypeSupport(MediaType mediaType, Class targetType) { return MediaTypeMatcher.TEXT_XML.mediaSupport(mediaType); } + @Override + public boolean typeSupport(Class targetType) { + return false; + } + + @Override + public MediaType contentType() { + return MediaType.TEXT_XML; + } + @Override public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception { Marshaller marshaller = JAXBContext.newInstance(unSerializedBody.getClass()).createMarshaller(); marshaller.marshal(unSerializedBody, outputStream); - outputStream.write((byte[]) unSerializedBody); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/ChunkOutputStream.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/ChunkOutputStream.java new file mode 100644 index 0000000000..53db275b7d --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/ChunkOutputStream.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.netty; + + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultHttpContent; + +import java.io.IOException; +import java.io.OutputStream; + +public class ChunkOutputStream extends OutputStream { + final ByteBuf buffer; + final ChannelHandlerContext ctx; + final NettyHttpResponse response; + + ChunkOutputStream(final NettyHttpResponse response, final ChannelHandlerContext ctx, final int chunksize) { + this.response = response; + if (chunksize < 1) { + throw new IllegalArgumentException(); + } + // TODO buffer pool + this.buffer = Unpooled.buffer(0, chunksize); + this.ctx = ctx; + } + + @Override + public void write(int b) throws IOException { + if (buffer.maxWritableBytes() < 1) { + flush(); + } + buffer.writeByte(b); + } + + public void reset() + { + if (response.isCommitted()) throw new IllegalStateException(); + buffer.clear(); + } + + @Override + public void close() throws IOException { + flush(); + super.close(); + } + + + @Override + public void write(byte[] b, int off, int len) throws IOException { + int dataLengthLeftToWrite = len; + int dataToWriteOffset = off; + int spaceLeftInCurrentChunk; + while ((spaceLeftInCurrentChunk = buffer.maxWritableBytes()) < dataLengthLeftToWrite) { + buffer.writeBytes(b, dataToWriteOffset, spaceLeftInCurrentChunk); + dataToWriteOffset = dataToWriteOffset + spaceLeftInCurrentChunk; + dataLengthLeftToWrite = dataLengthLeftToWrite - spaceLeftInCurrentChunk; + flush(); + } + if (dataLengthLeftToWrite > 0) { + buffer.writeBytes(b, dataToWriteOffset, dataLengthLeftToWrite); + } + } + + @Override + public void flush() throws IOException { + int readable = buffer.readableBytes(); + if (readable == 0) return; + if (!response.isCommitted()) response.prepareChunkStream(); + ctx.writeAndFlush(new DefaultHttpContent(buffer.copy())); + buffer.clear(); + super.flush(); + } + +} + diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/StreamUtils.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/HttpResponse.java similarity index 53% rename from dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/StreamUtils.java rename to dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/HttpResponse.java index 5b597c1724..890fe039b9 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/StreamUtils.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/HttpResponse.java @@ -14,28 +14,42 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.rpc.protocol.rest.util; +package org.apache.dubbo.rpc.protocol.rest.netty; + import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.Charset; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; -public class StreamUtils { - public static String copyToString(InputStream in, Charset charset) throws IOException { - if (in == null) { - return ""; - } else { - StringBuilder out = new StringBuilder(); - InputStreamReader reader = new InputStreamReader(in, charset); - char[] buffer = new char[4096]; - int bytesRead; - while ((bytesRead = reader.read(buffer)) != -1) { - out.append(buffer, 0, bytesRead); - } +public interface HttpResponse { + int getStatus(); + + void setStatus(int status); + + Map> getOutputHeaders(); + + OutputStream getOutputStream() throws IOException; + + void setOutputStream(OutputStream os); + + + void sendError(int status) throws IOException; + + void sendError(int status, String message) throws IOException; + + boolean isCommitted(); + + /** + * reset status and headers. Will fail if response is committed + */ + void reset(); + + void flushBuffer() throws IOException; + + + void addOutputHeaders(String name, String value); - return out.toString(); - } - } } + diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/NettyHttpResponse.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/NettyHttpResponse.java new file mode 100644 index 0000000000..c092907f3f --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/NettyHttpResponse.java @@ -0,0 +1,214 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.netty; + + +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.DefaultHttpResponse; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpHeaders.Names; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.LastHttpContent; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum; + + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; + + +/** + * netty http response + */ +public class NettyHttpResponse implements HttpResponse { + private static final int EMPTY_CONTENT_LENGTH = 0; + private int status = 200; + private OutputStream os; + private Map> outputHeaders; + private final ChannelHandlerContext ctx; + private boolean committed; + private boolean keepAlive; + private HttpMethod method; + + public NettyHttpResponse(final ChannelHandlerContext ctx, final boolean keepAlive) { + this(ctx, keepAlive, null); + } + + public NettyHttpResponse(final ChannelHandlerContext ctx, final boolean keepAlive, final HttpMethod method) { + outputHeaders = new HashMap<>(); + this.method = method; + // TODO chunk size to config + os = new ChunkOutputStream(this, ctx, 1000); + this.ctx = ctx; + this.keepAlive = keepAlive; + } + + + public void setOutputStream(OutputStream os) { + this.os = os; + } + + @Override + public int getStatus() { + return status; + } + + @Override + public void setStatus(int status) { + if (status > 200) { + addOutputHeaders(RestHeaderEnum.CONTENT_TYPE.getHeader(), MediaType.TEXT_PLAIN.value); + } + this.status = status; + } + + @Override + public Map> getOutputHeaders() { + return outputHeaders; + } + + @Override + public OutputStream getOutputStream() throws IOException { + return os; + } + + + @Override + public void sendError(int status) throws IOException { + sendError(status, null); + } + + @Override + public void sendError(int status, String message) throws IOException { + setStatus(status); + if (message != null) { + getOutputStream().write(message.getBytes(StandardCharsets.UTF_8)); + } + + } + + @Override + public boolean isCommitted() { + return committed; + } + + @Override + public void reset() { + if (committed) { + throw new IllegalStateException("Messages.MESSAGES.alreadyCommitted()"); + } + outputHeaders.clear(); + outputHeaders.clear(); + } + + public boolean isKeepAlive() { + return keepAlive; + } + + public DefaultHttpResponse getDefaultHttpResponse() { + DefaultHttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(getStatus())); + transformResponseHeaders(res); + return res; + } + + public DefaultHttpResponse getEmptyHttpResponse() { + DefaultFullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(getStatus())); + if (method == null || !method.equals(HttpMethod.HEAD)) { + res.headers().add(Names.CONTENT_LENGTH, EMPTY_CONTENT_LENGTH); + } + transformResponseHeaders(res); + + return res; + } + + private void transformResponseHeaders(io.netty.handler.codec.http.HttpResponse res) { + transformHeaders(this, res); + } + + + public void prepareChunkStream() { + committed = true; + DefaultHttpResponse response = getDefaultHttpResponse(); + HttpHeaders.setTransferEncodingChunked(response); + ctx.write(response); + } + + public void finish() throws IOException { + if (os != null) + os.flush(); + ChannelFuture future; + if (isCommitted()) { + // if committed this means the output stream was used. + future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); + } else { + future = ctx.writeAndFlush(getEmptyHttpResponse()); + } + + if (!isKeepAlive()) { + future.addListener(ChannelFutureListener.CLOSE); + } + + getOutputStream().close(); + } + + @Override + public void flushBuffer() throws IOException { + if (os != null) + os.flush(); + ctx.flush(); + } + + @Override + public void addOutputHeaders(String name, String value) { + + List values = outputHeaders.get(name); + + if (values == null) { + values = new ArrayList<>(); + outputHeaders.put(name, values); + } + + values.add(value); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public static void transformHeaders(NettyHttpResponse nettyResponse, io.netty.handler.codec.http.HttpResponse response) { +// if (nettyResponse.isKeepAlive()) { +// response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); +// } else { +// response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); +// } + + for (Map.Entry> entry : nettyResponse.getOutputHeaders().entrySet()) { + String key = entry.getKey(); + for (String value : entry.getValue()) { + response.headers().set(key, value); + } + } + + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/NettyServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/NettyServer.java new file mode 100644 index 0000000000..782072f6cc --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/NettyServer.java @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.rpc.protocol.rest.netty; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.timeout.IdleStateHandler; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.NamedThreadFactory; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; + +import java.net.InetSocketAddress; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.apache.dubbo.remoting.Constants.EVENT_LOOP_BOSS_POOL_NAME; +import static org.apache.dubbo.remoting.Constants.EVENT_LOOP_WORKER_POOL_NAME; + + +public class NettyServer { + + protected ServerBootstrap bootstrap = new ServerBootstrap(); + protected String hostname = null; + protected int configuredPort = 8080; + protected int runtimePort = -1; + + private EventLoopGroup eventLoopGroup; + private EventLoopGroup workerLoopGroup; + private int ioWorkerCount = Runtime.getRuntime().availableProcessors() * 2; + + private List channelHandlers = Collections.emptyList(); + private Map channelOptions = Collections.emptyMap(); + private Map childChannelOptions = Collections.emptyMap(); + private UnSharedHandlerCreator unSharedHandlerCallBack; + + public NettyServer() { + } + + + /** + * Specify the worker count to use. For more information about this please see the javadocs of {@link EventLoopGroup} + * + * @param ioWorkerCount worker count + */ + public void setIoWorkerCount(int ioWorkerCount) { + this.ioWorkerCount = ioWorkerCount; + } + + public String getHostname() { + return hostname; + } + + public void setHostname(String hostname) { + this.hostname = hostname; + } + + public int getPort() { + return runtimePort > 0 ? runtimePort : configuredPort; + } + + public void setPort(int port) { + this.configuredPort = port; + } + + + /** + * Add additional {@link io.netty.channel.ChannelHandler}s to the {@link io.netty.bootstrap.ServerBootstrap}. + *

The additional channel handlers are being added before the HTTP handling.

+ * + * @param channelHandlers the additional {@link io.netty.channel.ChannelHandler}s. + */ + public void setChannelHandlers(final List channelHandlers) { + this.channelHandlers = channelHandlers == null ? Collections.emptyList() : channelHandlers; + } + + /** + * Add Netty {@link io.netty.channel.ChannelOption}s to the {@link io.netty.bootstrap.ServerBootstrap}. + * + * @param channelOptions the additional {@link io.netty.channel.ChannelOption}s. + * @see io.netty.bootstrap.ServerBootstrap#option(io.netty.channel.ChannelOption, Object) + */ + public void setChannelOptions(final Map channelOptions) { + this.channelOptions = channelOptions == null ? Collections.emptyMap() : channelOptions; + } + + /** + * Add child options to the {@link io.netty.bootstrap.ServerBootstrap}. + * + * @param channelOptions the additional child {@link io.netty.channel.ChannelOption}s. + * @see io.netty.bootstrap.ServerBootstrap#childOption(io.netty.channel.ChannelOption, Object) + */ + public void setChildChannelOptions(final Map channelOptions) { + this.childChannelOptions = channelOptions == null ? Collections.emptyMap() : channelOptions; + } + + public void setUnSharedHandlerCallBack(UnSharedHandlerCreator unSharedHandlerCallBack) { + this.unSharedHandlerCallBack = unSharedHandlerCallBack; + } + + public void start(URL url) { + eventLoopGroup = new NioEventLoopGroup(1, new NamedThreadFactory(EVENT_LOOP_BOSS_POOL_NAME)); + workerLoopGroup = new NioEventLoopGroup(ioWorkerCount, new NamedThreadFactory(EVENT_LOOP_WORKER_POOL_NAME)); + + // Configure the server. + bootstrap.group(eventLoopGroup, workerLoopGroup) + .channel(NioServerSocketChannel.class) + .childHandler(setupHandlers(url)); + + + for (Map.Entry entry : channelOptions.entrySet()) { + bootstrap.option(entry.getKey(), entry.getValue()); + } + + for (Map.Entry entry : childChannelOptions.entrySet()) { + bootstrap.childOption(entry.getKey(), entry.getValue()); + } + + final InetSocketAddress socketAddress; + if (null == getHostname() || getHostname().isEmpty()) { + socketAddress = new InetSocketAddress(configuredPort); + } else { + socketAddress = new InetSocketAddress(hostname, configuredPort); + } + + Channel channel = bootstrap.bind(socketAddress).syncUninterruptibly().channel(); + runtimePort = ((InetSocketAddress) channel.localAddress()).getPort(); + } + + + protected ChannelHandler setupHandlers(URL url) { + + return new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline channelPipeline = ch.pipeline(); + + int idleTimeout = url.getParameter(RestConstant.IDLE_TIMEOUT_PARAM, RestConstant.IDLE_TIMEOUT); + if (idleTimeout > 0) { + channelPipeline.addLast(new IdleStateHandler(0, 0, idleTimeout)); + } + + channelPipeline.addLast(channelHandlers.toArray(new ChannelHandler[channelHandlers.size()])); + + List unSharedHandlers = unSharedHandlerCallBack.getUnSharedHandlers(url); + + for (ChannelHandler unSharedHandler : unSharedHandlers) { + channelPipeline.addLast(unSharedHandler); + } + + } + }; + + } + + + public void stop() { + runtimePort = -1; + eventLoopGroup.shutdownGracefully(); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/RestHttpRequestDecoder.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/RestHttpRequestDecoder.java new file mode 100644 index 0000000000..7ff4e63a85 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/RestHttpRequestDecoder.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.rpc.protocol.rest.netty; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageDecoder; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.Executor; + +import io.netty.handler.codec.http.HttpHeaders; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.threadpool.ThreadPool; +import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum; +import org.apache.dubbo.rpc.protocol.rest.handler.NettyHttpHandler; +import org.apache.dubbo.rpc.protocol.rest.request.NettyRequestFacade; + + +public class RestHttpRequestDecoder extends MessageToMessageDecoder { + private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); + + private final NettyHttpHandler handler; + private final Executor executor; + + + public RestHttpRequestDecoder(NettyHttpHandler handler, URL url) { + this.handler = handler; + executor = url.getOrDefaultFrameworkModel().getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url); + } + + + @Override + protected void decode(ChannelHandlerContext ctx, io.netty.handler.codec.http.FullHttpRequest request, List out) throws Exception { + boolean keepAlive = HttpHeaders.isKeepAlive(request); + + NettyHttpResponse nettyHttpResponse = new NettyHttpResponse(ctx, keepAlive); + NettyRequestFacade requestFacade = new NettyRequestFacade(request, ctx); + + executor.execute(() -> { + + // business handler + try { + handler.handle(requestFacade, nettyHttpResponse); + + } catch (IOException e) { + logger.error("", e.getCause().getMessage(), "dubbo rest rest http request handler error", e.getMessage(), e); + } finally { + // write response + try { + nettyHttpResponse.addOutputHeaders(RestHeaderEnum.CONNECTION.getHeader(), "close"); + nettyHttpResponse.finish(); + } catch (IOException e) { + logger.error("", e.getCause().getMessage(), "dubbo rest rest http response flush error", e.getMessage(), e); + } + } + + }); + + + } +} + diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/UnSharedHandlerCreator.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/UnSharedHandlerCreator.java new file mode 100644 index 0000000000..24fce27ab6 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/UnSharedHandlerCreator.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.netty; + +import io.netty.channel.ChannelHandler; +import org.apache.dubbo.common.URL; + +import java.util.List; + +/** + * FOR create netty un shared (no @Shared) handler + */ +public interface UnSharedHandlerCreator { + + List getUnSharedHandlers(URL url); +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/pair/InvokerAndRestMethodMetadataPair.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/pair/InvokerAndRestMethodMetadataPair.java new file mode 100644 index 0000000000..0075275e49 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/pair/InvokerAndRestMethodMetadataPair.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.pair; + +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.rpc.Invoker; + +/** + * for invoker & restMethodMetadata pair + */ +public class InvokerAndRestMethodMetadataPair { + + Invoker invoker; + RestMethodMetadata restMethodMetadata; + + public InvokerAndRestMethodMetadataPair(Invoker invoker, RestMethodMetadata restMethodMetadata) { + this.invoker = invoker; + this.restMethodMetadata = restMethodMetadata; + } + + public Invoker getInvoker() { + return invoker; + } + + public RestMethodMetadata getRestMethodMetadata() { + return restMethodMetadata; + } + + + public static InvokerAndRestMethodMetadataPair pair(Invoker invoker, RestMethodMetadata restMethodMetadata) { + return new InvokerAndRestMethodMetadataPair(invoker, restMethodMetadata); + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/pair/MessageCodecResultPair.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/pair/MessageCodecResultPair.java new file mode 100644 index 0000000000..00b3154c0a --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/pair/MessageCodecResultPair.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.pair; + +import org.apache.dubbo.metadata.rest.media.MediaType; + +/** + * for http message codec result + */ +public class MessageCodecResultPair { + /** + * has coded + */ + boolean coded; + + /** + * codec type + */ + MediaType mediaType; + + + public MessageCodecResultPair(boolean coded, MediaType mediaType) { + this.coded = coded; + this.mediaType = mediaType; + } + + + public boolean isCoded() { + return coded; + } + + public MediaType getMediaType() { + return mediaType; + } + + public static MessageCodecResultPair pair(boolean coded, MediaType mediaType) { + return new MessageCodecResultPair(coded, mediaType); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/request/NettyRequestFacade.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/request/NettyRequestFacade.java new file mode 100644 index 0000000000..b14178bf72 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/request/NettyRequestFacade.java @@ -0,0 +1,253 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.request; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpContent; +import org.apache.dubbo.common.utils.IOUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +/** + * netty request facade + */ +public class NettyRequestFacade extends RequestFacade { + + + private ChannelHandlerContext context; + + + public NettyRequestFacade(Object request, ChannelHandlerContext context) { + super((FullHttpRequest) request); + this.context = context; + + } + + + protected void initHeaders() { + for (Map.Entry header : request.headers()) { + + String key = header.getKey(); + + ArrayList tmpHeaders = headers.get(key); + + if (tmpHeaders == null) { + tmpHeaders = new ArrayList<>(); + headers.put(key, tmpHeaders); + } + + tmpHeaders.add(header.getValue()); + } + } + + @Override + public String getHeader(String name) { + + List values = headers.get(name); + + if (values == null || values.isEmpty()) { + return null; + } else { + return values.get(0); + } + + } + + @Override + public Enumeration getHeaders(String name) { + + List list = headers.get(name); + + if (list == null) { + list = new ArrayList<>(); + } + + + ListIterator stringListIterator = list.listIterator(); + + return new Enumeration() { + @Override + public boolean hasMoreElements() { + return stringListIterator.hasNext(); + } + + @Override + public String nextElement() { + return stringListIterator.next(); + } + }; + } + + + @Override + public Enumeration getHeaderNames() { + + Iterator strings = headers.keySet().iterator(); + + return new Enumeration() { + @Override + public boolean hasMoreElements() { + return strings.hasNext(); + } + + @Override + public String nextElement() { + return strings.next(); + } + }; + } + + @Override + public String getMethod() { + return request.method().name(); + } + + @Override + public String getPath() { + return path; + } + + @Override + public String getContextPath() { + // TODO add ContextPath + return null; + } + + + @Override + public String getRequestURI() { + return request.uri(); + } + + + @Override + public String getParameter(String name) { + ArrayList strings = parameters.get(name); + + String value = null; + if (strings != null && !strings.isEmpty()) { + value = strings.get(0); + + } + return value; + } + + @Override + public Enumeration getParameterNames() { + + Iterator iterator = parameters.keySet().iterator(); + + return new Enumeration() { + @Override + public boolean hasMoreElements() { + return iterator.hasNext(); + } + + @Override + public String nextElement() { + return iterator.next(); + } + }; + + } + + @Override + public String[] getParameterValues(String name) { + + if (!parameters.containsKey(name)) { + + return null; + } + return parameters.get(name).toArray(new String[0]); + } + + @Override + public Map getParameterMap() { + HashMap map = new HashMap<>(); + parameters.entrySet().forEach(entry -> { + map.put(entry.getKey(), entry.getValue().toArray(new String[0])); + }); + return map; + + } + + + @Override + public String getRemoteAddr() { + return getChannel().remoteAddress().getHostString(); + } + + @Override + public String getRemoteHost() { + return getRemoteAddr() + ":" + getRemotePort(); + } + + @Override + public int getRemotePort() { + return getChannel().remoteAddress().getPort(); + } + + @Override + public String getLocalAddr() { + return getChannel().localAddress().getHostString(); + } + + @Override + public String getLocalHost() { + return getRemoteAddr() + ":" + getLocalPort(); + } + + private NioSocketChannel getChannel() { + return (NioSocketChannel) context.channel(); + } + + @Override + public int getLocalPort() { + return getChannel().localAddress().getPort(); + } + + @Override + public byte[] getInputStream() throws IOException { + + return body; + } + + protected void parseBody() { + ByteBuf byteBuf = ((HttpContent) request).content(); + + if (byteBuf.readableBytes() > 0) { + + try { + body = IOUtils.toByteArray(new ByteBufInputStream(byteBuf)); + } catch (IOException e) { + + } + } + + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/request/RequestFacade.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/request/RequestFacade.java new file mode 100644 index 0000000000..8d95ac60cb --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/request/RequestFacade.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.request; + + +import org.apache.dubbo.common.utils.StringUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +/** + * request facade for different request + * + * @param + */ +public abstract class RequestFacade { + protected Map> headers = new HashMap<>(); + protected Map> parameters = new HashMap<>(); + + protected String path; + protected T request; + protected byte[] body = new byte[0]; + + public RequestFacade(T request) { + this.request = request; + initHeaders(); + initParameters(); + parseBody(); + } + + protected void initHeaders() { + + } + + + protected void initParameters() { + String requestURI = getRequestURI(); + + if (requestURI != null && requestURI.contains("?")) { + + String queryString = requestURI.substring(requestURI.indexOf("?") + 1); + path = requestURI.substring(0, requestURI.indexOf("?")); + + String[] split = queryString.split("&"); + + for (String params : split) { + // key a= ;value b==c + int index = params.indexOf("="); + if (index <= 0) { + continue; + } + + String name = params.substring(0, index); + String value = params.substring(index + 1); + if (!StringUtils.isEmpty(name)) { + ArrayList values = parameters.get(name); + + if (values == null) { + values = new ArrayList<>(); + parameters.put(name, values); + } + values.add(value); + + } + } + } else { + path = requestURI; + } + } + + + public T getRequest() { + return request; + } + + public abstract String getHeader(String name); + + + public abstract Enumeration getHeaders(String name); + + + public abstract Enumeration getHeaderNames(); + + public abstract String getMethod(); + + + public abstract String getPath(); + + public abstract String getContextPath(); + + public abstract String getRequestURI(); + + public abstract String getParameter(String name); + + public abstract Enumeration getParameterNames(); + + public abstract String[] getParameterValues(String name); + + public abstract Map getParameterMap(); + + public abstract String getRemoteAddr(); + + public abstract String getRemoteHost(); + + public abstract int getRemotePort(); + + public abstract String getLocalAddr(); + + public abstract String getLocalHost(); + + public abstract int getLocalPort(); + + public abstract byte[] getInputStream() throws IOException; + + protected abstract void parseBody(); + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/support/LoggingFilter.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/support/LoggingFilter.java deleted file mode 100644 index 37d42b1c3c..0000000000 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/support/LoggingFilter.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.rpc.protocol.rest.support; - -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; - -import org.apache.commons.io.IOUtils; - -import javax.annotation.Priority; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.client.ClientRequestContext; -import javax.ws.rs.client.ClientRequestFilter; -import javax.ws.rs.client.ClientResponseContext; -import javax.ws.rs.client.ClientResponseFilter; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerRequestFilter; -import javax.ws.rs.container.ContainerResponseContext; -import javax.ws.rs.container.ContainerResponseFilter; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.ext.ReaderInterceptor; -import javax.ws.rs.ext.ReaderInterceptorContext; -import javax.ws.rs.ext.WriterInterceptor; -import javax.ws.rs.ext.WriterInterceptorContext; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Map; - -/** - * This logging filter is not highly optimized for now - * - */ -@Priority(Integer.MIN_VALUE) -public class LoggingFilter implements ContainerRequestFilter, ClientRequestFilter, ContainerResponseFilter, ClientResponseFilter, WriterInterceptor, ReaderInterceptor { - - private static final Logger logger = LoggerFactory.getLogger(LoggingFilter.class); - - @Override - public void filter(ClientRequestContext context) throws IOException { - logHttpHeaders(context.getStringHeaders()); - } - - @Override - public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { - logHttpHeaders(responseContext.getHeaders()); - } - - @Override - public void filter(ContainerRequestContext context) throws IOException { - logHttpHeaders(context.getHeaders()); - } - - @Override - public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { - logHttpHeaders(responseContext.getStringHeaders()); - } - - @Override - public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException { - byte[] buffer = IOUtils.toByteArray(context.getInputStream()); - logger.info("The contents of request body is: \n" + new String(buffer, StandardCharsets.UTF_8) + "\n"); - context.setInputStream(new ByteArrayInputStream(buffer)); - return context.proceed(); - } - - @Override - public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException { - OutputStreamWrapper wrapper = new OutputStreamWrapper(context.getOutputStream()); - context.setOutputStream(wrapper); - context.proceed(); - logger.info("The contents of response body is: \n" + new String(wrapper.getBytes(), StandardCharsets.UTF_8) + "\n"); - } - - protected void logHttpHeaders(MultivaluedMap headers) { - StringBuilder msg = new StringBuilder("The HTTP headers are: \n"); - for (Map.Entry> entry : headers.entrySet()) { - msg.append(entry.getKey()).append(": "); - for (int i = 0; i < entry.getValue().size(); i++) { - msg.append(entry.getValue().get(i)); - if (i < entry.getValue().size() - 1) { - msg.append(", "); - } - } - msg.append('\n'); - } - logger.info(msg.toString()); - } - - protected static class OutputStreamWrapper extends OutputStream { - - private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - private final OutputStream output; - - private OutputStreamWrapper(OutputStream output) { - this.output = output; - } - - @Override - public void write(int i) throws IOException { - buffer.write(i); - output.write(i); - } - - @Override - public void write(byte[] b) throws IOException { - buffer.write(b); - output.write(b); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - buffer.write(b, off, len); - output.write(b, off, len); - } - - @Override - public void flush() throws IOException { - output.flush(); - } - - @Override - public void close() throws IOException { - output.close(); - } - - public byte[] getBytes() { - return buffer.toByteArray(); - } - } -} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/DataParseUtils.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/DataParseUtils.java index d5e587cd93..4501c7972d 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/DataParseUtils.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/DataParseUtils.java @@ -17,6 +17,7 @@ package org.apache.dubbo.rpc.protocol.rest.util; import org.apache.dubbo.common.utils.JsonUtils; +import org.apache.dubbo.common.utils.StringUtils; import java.io.IOException; import java.io.OutputStream; @@ -33,10 +34,13 @@ import java.util.StringTokenizer; public class DataParseUtils { - public static Object stringTypeConvert(Class targetType, String value) { + public static Object stringTypeConvert(Class targetType, String value) { + if (StringUtils.isEmpty(value)) { + return null; + } - if (targetType == Boolean.class) { + if (targetType == Boolean.class || targetType == boolean.class) { return Boolean.valueOf(value); } @@ -48,10 +52,24 @@ public class DataParseUtils { return NumberUtils.parseNumber(value, targetType); } + if (targetType != null && targetType.isPrimitive()) { + return NumberUtils.parseNumber(value, targetType); + } + return value; } + public static boolean isTextType(Class targetType) { + if (targetType == null) { + return false; + } + + return targetType == Boolean.class || targetType == boolean.class || + targetType == String.class || + Number.class.isAssignableFrom(targetType) || targetType.isPrimitive(); + } + /** * content-type text @@ -72,7 +90,7 @@ public class DataParseUtils { * @throws Exception */ public static void writeJsonContent(Object object, OutputStream outputStream) throws Exception { - outputStream.write(JsonUtils.getJson().toJson(object).getBytes(StandardCharsets.UTF_8)); + outputStream.write(JsonUtils.toJson(object).getBytes(StandardCharsets.UTF_8)); } /** @@ -117,7 +135,7 @@ public class DataParseUtils { public static byte[] objectTextConvertToByteArray(Object object) { Class objectClass = object.getClass(); - if (objectClass == Boolean.class) { + if (objectClass == Boolean.class || objectClass == boolean.class) { return object.toString().getBytes(); } @@ -125,26 +143,7 @@ public class DataParseUtils { return ((String) object).getBytes(); } - if (objectClass.isAssignableFrom(Number.class)) { - return (byte[]) NumberUtils.numberToBytes((Number) object); - } - - return object.toString().getBytes(); - - } - - public static byte[] objectJsonConvertToByteArray(Object object) { - Class objectClass = object.getClass(); - - if (objectClass == Boolean.class) { - return object.toString().getBytes(); - } - - if (objectClass == String.class) { - return ((String) object).getBytes(); - } - - if (objectClass.isAssignableFrom(Number.class)) { + if (objectClass.isAssignableFrom(Number.class) || objectClass.isPrimitive()) { return (byte[]) NumberUtils.numberToBytes((Number) object); } @@ -153,13 +152,13 @@ public class DataParseUtils { } public static Object jsonConvert(Class targetType, byte[] body) throws Exception { - return JsonUtils.getJson().toJavaObject(new String(body, StandardCharsets.UTF_8), targetType); + return JsonUtils.toJavaObject(new String(body, StandardCharsets.UTF_8), targetType); } - public static Object multipartFormConvert(byte[] body, Charset charset) throws Exception { + public static Object multipartFormConvert(byte[] body, Charset charset, Class targetType) throws Exception { String[] pairs = tokenizeToStringArray(new String(body, StandardCharsets.UTF_8), "&"); - Object result = MultiValueCreator.createMultiValueMap(); + Object result = MultiValueCreator.providerCreateMultiValueMap(targetType); for (String pair : pairs) { int idx = pair.indexOf('='); if (idx == -1) { @@ -174,8 +173,8 @@ public class DataParseUtils { return result; } - public static Object multipartFormConvert(byte[] body) throws Exception { - return multipartFormConvert(body, Charset.defaultCharset()); + public static Object multipartFormConvert(byte[] body, Class targetType) throws Exception { + return multipartFormConvert(body, Charset.defaultCharset(), targetType); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/HttpHeaderUtil.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/HttpHeaderUtil.java new file mode 100644 index 0000000000..194d7de464 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/HttpHeaderUtil.java @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.rpc.protocol.rest.util; + +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.remoting.http.RestResult; +import org.apache.dubbo.rpc.AppResponse; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; +import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse; +import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class HttpHeaderUtil { + + + /** + * convert attachment to Map> + * + * @param attachmentMap + * @return + */ + public static Map> createAttachments(Map attachmentMap) { + Map> attachments = new HashMap<>(); + int size = 0; + for (Map.Entry entry : attachmentMap.entrySet()) { + String key = entry.getKey(); + String value = String.valueOf(entry.getValue()); + + if (value != null) { + size += value.getBytes(StandardCharsets.UTF_8).length; + } + + List strings = attachments.get(key); + if (strings == null) { + strings = new ArrayList<>(); + attachments.put(key, strings); + } + strings.add(value); + } + + return attachments; + } + + + /** + * add consumer attachment to request + * + * @param requestTemplate + * @param attachmentMap + */ + public static void addRequestAttachments(RequestTemplate requestTemplate, Map attachmentMap) { + Map> attachments = createAttachments(attachmentMap); + + attachments.entrySet().forEach(attachment -> { + requestTemplate.addHeaders(appendPrefixToAttachRealHeader(attachment.getKey()), attachment.getValue()); + }); + + } + + + /** + * add provider attachment to response + * + * @param nettyHttpResponse + */ + public static void addResponseAttachments(NettyHttpResponse nettyHttpResponse) { + Map> attachments = createAttachments(RpcContext.getServerContext().getObjectAttachments()); + + attachments.entrySet().stream().forEach(attachment -> { + nettyHttpResponse.getOutputHeaders().put(appendPrefixToAttachRealHeader(attachment.getKey()), attachment.getValue()); + }); + } + + + /** + * parse rest request header attachment & header + * + * @param rpcInvocation + * @param requestFacade + */ + public static void parseRequestHeader(RpcInvocation rpcInvocation, RequestFacade requestFacade) { + + Enumeration headerNames = requestFacade.getHeaderNames(); + + while (headerNames.hasMoreElements()) { + String header = headerNames.nextElement(); + + if (!isRestAttachHeader(header)) { + // attribute + rpcInvocation.put(header, requestFacade.getHeader(header)); + continue; + } + + // attachment + rpcInvocation.setAttachment(subRestAttachRealHeaderPrefix(header.trim()), requestFacade.getHeader(header)); + + } + } + + + /** + * for judge rest header or rest attachment + * + * @param header + * @return + */ + public static boolean isRestAttachHeader(String header) { + + if (StringUtils.isEmpty(header) || !header.startsWith(RestHeaderEnum.REST_HEADER_PREFIX.getHeader())) { + return false; + } + + return true; + } + + /** + * for substring attachment prefix + * + * @param header + * @return + */ + public static String subRestAttachRealHeaderPrefix(String header) { + + return header.substring(RestHeaderEnum.REST_HEADER_PREFIX.getHeader().length()); + } + + /** + * append prefix to rest header distinguish from normal header + * + * @param header + * @return + */ + public static String appendPrefixToAttachRealHeader(String header) { + + return RestHeaderEnum.REST_HEADER_PREFIX.getHeader() + header; + } + + + /** + * parse request attribute + * @param rpcInvocation + * @param request + */ + public static void parseRequestAttribute(RpcInvocation rpcInvocation, RequestFacade request) { + int localPort = request.getLocalPort(); + String localAddr = request.getLocalAddr(); + int remotePort = request.getRemotePort(); + String remoteAddr = request.getRemoteAddr(); + + rpcInvocation.put(RestConstant.REMOTE_ADDR, remoteAddr); + rpcInvocation.put(RestConstant.LOCAL_ADDR, localAddr); + rpcInvocation.put(RestConstant.REMOTE_PORT, remotePort); + rpcInvocation.put(RestConstant.LOCAL_PORT, localPort); + } + + + /** + * parse request + * @param rpcInvocation + * @param request + */ + public static void parseRequest(RpcInvocation rpcInvocation, RequestFacade request) { + parseRequestHeader(rpcInvocation, request); + parseRequestAttribute(rpcInvocation, request); + } + + /** + * parse rest response header to appResponse attribute & attachment + * @param appResponse + * @param restResult + */ + public static void parseResponseHeader(AppResponse appResponse, RestResult restResult) { + + Map> headers = restResult.headers(); + if (headers == null || headers.isEmpty()) { + return; + } + + headers.entrySet().stream().forEach(entry -> { + String header = entry.getKey(); + if (isRestAttachHeader(header)) { + // attachment + appResponse.setAttachment(subRestAttachRealHeaderPrefix(header), entry.getValue()); + } else { + // attribute + appResponse.setAttribute(header, entry.getValue()); + } + }); + + + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MediaTypeUtil.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MediaTypeUtil.java index 4a16ea5861..55a9783b12 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MediaTypeUtil.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MediaTypeUtil.java @@ -18,14 +18,27 @@ package org.apache.dubbo.rpc.protocol.rest.util; import org.apache.dubbo.metadata.rest.media.MediaType; import org.apache.dubbo.rpc.protocol.rest.exception.UnSupportContentTypeException; +import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager; import java.util.Arrays; import java.util.List; public class MediaTypeUtil { - private static final List mediaTypes = Arrays.asList(MediaType.values()); - public static MediaType convertMediaType(String... contentTypes) { + private static final List mediaTypes = MediaType.getSupportMediaTypes(); + + + /** + * return first match , if any multiple content-type ,acquire mediaType by targetClass type .if contentTypes is empty + * + * @param contentTypes + * @return + */ + public static MediaType convertMediaType(Class targetType, String... contentTypes) { + + if (contentTypes == null || contentTypes.length == 0) { + return HttpMessageCodecManager.typeSupport(targetType); + } for (String contentType : contentTypes) { for (MediaType mediaType : mediaTypes) { @@ -34,6 +47,10 @@ public class MediaTypeUtil { return mediaType; } } + + if (contentType != null && contentType.contains(MediaType.ALL_VALUE.value)) { + return HttpMessageCodecManager.typeSupport(targetType); + } } throw new UnSupportContentTypeException(Arrays.toString(contentTypes)); diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MultiValueCreator.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MultiValueCreator.java index e64f47af73..8a391b9955 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MultiValueCreator.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MultiValueCreator.java @@ -17,37 +17,86 @@ package org.apache.dubbo.rpc.protocol.rest.util; +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; + import java.lang.reflect.Method; +import java.util.Map; public class MultiValueCreator { - private final static String SPRING_MultiValueMap = "org.springframework.util.LinkedMultiValueMap"; - private final static String JAVAX_MultiValueMap = "org.jboss.resteasy.specimpl.MultivaluedMapImpl"; + private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MultiValueCreator.class); - private static Class multiValueMapClass = null; - private static Method multiValueMapAdd = null; + private final static String SPRING_MultiValueMapImpl = "org.springframework.util.LinkedMultiValueMap"; + private final static String SPRING_MultiValueMap = "org.springframework.util.MultiValueMap"; + private final static String JAVAX_MultiValueMapImpl = "org.jboss.resteasy.specimpl.MultivaluedMapImpl"; + private final static String JAVAX_MultiValueMap = "javax.ws.rs.core.MultivaluedMap"; + + private static Class springMultiValueMapImplClass = null; + private static Class springMultiValueMapClass = null; + private static Method springMultiValueMapAdd = null; + + private static Class jaxrsMultiValueMapImplClass = null; + private static Class jaxrsMultiValueMapClass = null; + + private static Method jaxrsMultiValueMapAdd = null; static { - multiValueMapClass = ReflectUtils.findClassTryException(SPRING_MultiValueMap, JAVAX_MultiValueMap); - multiValueMapAdd = ReflectUtils.getMethodAndTryCatch(multiValueMapClass, "add", new Class[]{Object.class, Object.class}); + springMultiValueMapClass = ReflectUtils.findClassTryException(SPRING_MultiValueMap); + springMultiValueMapImplClass = ReflectUtils.findClassTryException(SPRING_MultiValueMapImpl); + springMultiValueMapAdd = ReflectUtils.getMethodByName(springMultiValueMapImplClass, "add"); + + jaxrsMultiValueMapClass = ReflectUtils.findClassTryException(JAVAX_MultiValueMap); + jaxrsMultiValueMapImplClass = ReflectUtils.findClassTryException(JAVAX_MultiValueMapImpl); + jaxrsMultiValueMapAdd = ReflectUtils.getMethodByName(jaxrsMultiValueMapImplClass, "add"); + } - public static Object createMultiValueMap() { + public static Object providerCreateMultiValueMap(Class targetType) { try { - return multiValueMapClass.getDeclaredConstructor().newInstance(); + if (typeJudge(springMultiValueMapClass, targetType)) { + return springMultiValueMapImplClass.getDeclaredConstructor().newInstance(); + } else if (typeJudge(jaxrsMultiValueMapClass, targetType)) { + return jaxrsMultiValueMapImplClass.getDeclaredConstructor().newInstance(); + } } catch (Exception e) { - + logger.error("", e.getMessage(), "current param type is: " + targetType + "and support type is : " + springMultiValueMapClass + "or" + jaxrsMultiValueMapClass, + "dubbo rest form content-type param construct error,un support param type: ", e); } return null; } - public static void add(Object multiValueMap, String key, String value) { - try { - ReflectUtils.invokeAndTryCatch(multiValueMap, multiValueMapAdd, new String[]{key, value}); - } catch (Exception e) { + private static boolean typeJudge(Class parent, Class targetType) { + if (parent == null) { + return false; + } + + if (!Map.class.isAssignableFrom(targetType)) { + return true; + } + + return parent.isAssignableFrom(targetType) || parent.equals(targetType); + } + + public static void add(Object multiValueMap, String key, Object value) { + try { + if (multiValueMap == null) { + return; + } + + Method multiValueMapAdd = null; + if (springMultiValueMapImplClass.equals(multiValueMap.getClass())) { + multiValueMapAdd = springMultiValueMapAdd; + } else if (jaxrsMultiValueMapImplClass.equals(multiValueMap.getClass())) { + multiValueMapAdd = jaxrsMultiValueMapAdd; + } + + ReflectUtils.invokeAndTryCatch(multiValueMap, multiValueMapAdd, new Object[]{key, value}); + } catch (Exception e) { + logger.error("", e.getMessage(), "", "dubbo rest form content-type param add data error: ", e); } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/NumberUtils.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/NumberUtils.java index 47aa746bbb..77a87b7308 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/NumberUtils.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/NumberUtils.java @@ -21,47 +21,27 @@ import org.apache.dubbo.common.utils.StringUtils; import java.math.BigDecimal; import java.math.BigInteger; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; public class NumberUtils { - - public static final Set> STANDARD_NUMBER_TYPES; - - static { - Set> numberTypes = new HashSet<>(8); - numberTypes.add(Byte.class); - numberTypes.add(Short.class); - numberTypes.add(Integer.class); - numberTypes.add(Long.class); - numberTypes.add(BigInteger.class); - numberTypes.add(Float.class); - numberTypes.add(Double.class); - numberTypes.add(BigDecimal.class); - STANDARD_NUMBER_TYPES = Collections.unmodifiableSet(numberTypes); - } - - public static T parseNumber(String text, Class targetClass) { Assert.notNull(text, "Text must not be null"); Assert.notNull(targetClass, "Target class must not be null"); String trimmed = trimAllWhitespace(text); - if (Byte.class == targetClass) { + if (Byte.class == targetClass || byte.class == targetClass) { return (T) (isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed)); - } else if (Short.class == targetClass) { + } else if (Short.class == targetClass || short.class == targetClass) { return (T) (isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed)); - } else if (Integer.class == targetClass) { + } else if (Integer.class == targetClass || int.class == targetClass) { return (T) (isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed)); - } else if (Long.class == targetClass) { + } else if (Long.class == targetClass || long.class == targetClass) { return (T) (isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed)); } else if (BigInteger.class == targetClass) { return (T) (isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed)); - } else if (Float.class == targetClass) { + } else if (Float.class == targetClass || float.class == targetClass) { return (T) Float.valueOf(trimmed); - } else if (Double.class == targetClass) { + } else if (Double.class == targetClass || double.class == targetClass) { return (T) Double.valueOf(trimmed); } else if (BigDecimal.class == targetClass || Number.class == targetClass) { return (T) new BigDecimal(trimmed); diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ReflectUtils.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ReflectUtils.java index 23b71540e7..4da7589e99 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ReflectUtils.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ReflectUtils.java @@ -17,49 +17,20 @@ package org.apache.dubbo.rpc.protocol.rest.util; -import java.lang.reflect.Array; -import java.lang.reflect.Field; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class ReflectUtils { - private final static Class[] EMPTY_CLASS_ARRAY = new Class[0]; - private final static Object[] EMPTY_OBJECT_ARRAY = new Object[0]; - public static Field getField(Class clazz, String field) throws IllegalAccessException { - Field[] fields = clazz.getDeclaredFields(); + public static Class findClass(String name, ClassLoader classLoader) throws ClassNotFoundException { - for (Field field1 : fields) { - if (field1.getName().equals(field)) { - return setModifiersUnFinal(field1); - } - } + return classLoader.loadClass(name); - fields = clazz.getFields(); - - for (Field field1 : fields) { - if (field1.getName().equals(field)) { - return setModifiersUnFinal(field1); - } - } - - return null; - } - - - public static Method getMethod(Class clazz, String method, Class[] paramTypes) throws NoSuchMethodException { - Method declaredMethod = clazz.getDeclaredMethod(method, paramTypes); - declaredMethod.setAccessible(true); - return declaredMethod; - } - - private static Field setModifiersUnFinal(Field field) throws IllegalAccessException { - // public -// MODIFIERS.set(field, 1); - field.setAccessible(true); - return field; } public static Class findClass(String name) throws ClassNotFoundException { @@ -68,12 +39,6 @@ public class ReflectUtils { } - public static Class findClass(String name, ClassLoader classLoader) throws ClassNotFoundException { - - return classLoader.loadClass(name); - - } - public static Class findClassAndTryCatch(String name, ClassLoader classLoader) { try { @@ -85,12 +50,6 @@ public class ReflectUtils { } - public static Class findClass(String... name) throws ClassNotFoundException { - - return findClass(Thread.currentThread().getContextClassLoader(), name); - } - - public static Class findClass(ClassLoader classLoader, String... name) throws ClassNotFoundException { String[] names = name; @@ -119,79 +78,79 @@ public class ReflectUtils { } + public static List getMethodByNameList(Class clazz, String name) { + // prevent duplicate method + Set methods = new HashSet<>(); + + try { + filterMethod(name, methods, clazz.getDeclaredMethods()); + + } catch (Exception e) { + + } + + try { + filterMethod(name, methods, clazz.getMethods()); + } catch (Exception e) { + + } + return new ArrayList<>(methods); + + + } + + public static List> getConstructList(Class clazz) { + // prevent duplicate method + Set> methods = new HashSet<>(); + + try { + filterConstructMethod(methods, clazz.getDeclaredConstructors()); + } catch (Exception e) { + } + + try { + filterConstructMethod(methods, clazz.getConstructors()); + } catch (Exception e) { + + } + return new ArrayList>(methods); + + + } + + private static void filterConstructMethod(Set> methods, Constructor[] declaredMethods) { + for (Constructor constructor : declaredMethods) { + methods.add(constructor); + } + + } + + private static void filterMethod(String name, Set methodList, Method[] methods) { + for (Method declaredMethod : methods) { + if (!name.equals(declaredMethod.getName())) { + continue; + } + declaredMethod.setAccessible(true); + methodList.add(declaredMethod); + } + } + + public static Method getMethodByName(Class clazz, String name) { + + List methodByNameList = getMethodByNameList(clazz, name); + if (methodByNameList.isEmpty()) { + return null; + } else { + return methodByNameList.get(0); + } + } + public static Class findClassTryException(String... name) { return findClassTryException(Thread.currentThread().getContextClassLoader(), name); } - - public static Object getArrayElement(Object obj, int index) { - return Array.get(obj, index); - } - - public static List getArrayElements(Object obj) { - - List objects = new ArrayList(); - int length = Array.getLength(obj); - - if (length == 0) { - return objects; - } - - for (int i = 0; i < length; i++) { - objects.add(Array.get(obj, i)); - } - - return objects; - } - - - public static Field getFieldAndTryCatch(Class clazz, String field) { - - try { - return getField(clazz, field); - } catch (Exception e) { - - } - return null; - } - - public static Method getMethod(Class clazz, String method) throws NoSuchMethodException { - Method declaredMethod = clazz.getDeclaredMethod(method, new Class[]{}); - declaredMethod.setAccessible(true); - return declaredMethod; - } - - public static Method getMethodAndTry(Class clazz, String method) { - Method declaredMethod = null; - try { - declaredMethod = clazz.getDeclaredMethod(method, EMPTY_CLASS_ARRAY); - declaredMethod.setAccessible(true); - } catch (Exception e) { - - } - - if (declaredMethod == null) { - try { - declaredMethod = getMethodByName(clazz, method); - } catch (Exception e) { - - } - } - - return declaredMethod; - } - - public static Object invoke(Object object, Method method) { - try { - return method.invoke(object, EMPTY_OBJECT_ARRAY); - } catch (Exception e) { - return null; - } - } - public static Object invoke(Object object, Method method, Object[] params) throws InvocationTargetException, IllegalAccessException { return method.invoke(object, params); - } public static Object invokeAndTryCatch(Object object, Method method, Object[] params) { @@ -204,66 +163,5 @@ public class ReflectUtils { return null; } - public static Class findClassAndTry(String name) { - - try { - return findClass(name); - } catch (Exception e) { - return null; - } - - } - - public static Method getMethodByName(Class clazz, String name) { - Method[] declaredMethods = clazz.getMethods(); - - for (Method declaredMethod : declaredMethods) { - if (name.equals(declaredMethod.getName())) { - - return declaredMethod; - } - } - - return null; - - } - - - public static Object getValueByFields(Object obj, Field... fields) { - for (Field field : fields) { - - try { - Object o = field.get(obj); - if (o != null) { - return o; - } - } catch (Exception e) { - - } - - } - - return null; - } - - public static Method getMethodAndTryCatch(Class clazz, String method, Class[] paramTypes) { - try { - return getMethod(clazz, method, paramTypes); - } catch (Throwable e) { - - } - return null; - - } - - - public static Object getFieldValueAndTryCatch(Object obj, String field) { - try { - return ReflectUtils.getValueByFields(obj, ReflectUtils.getFieldAndTryCatch(obj.getClass(), field)); - } catch (Exception e) { - return null; - } - } - } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.BaseProviderParamParser b/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.BaseProviderParamParser new file mode 100644 index 0000000000..0f62f51f54 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.BaseProviderParamParser @@ -0,0 +1,4 @@ +body=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.BodyProviderParamParser +header=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.HeaderProviderParamParser +path=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.PathProviderParamParser +param=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.ParamProviderParamParser diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DataParseUtilsTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DataParseUtilsTest.java new file mode 100644 index 0000000000..ae29327019 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DataParseUtilsTest.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest; + +import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; + +public class DataParseUtilsTest { + @Test + void testJsonConvert() throws Exception { + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + DataParseUtils.writeJsonContent(User.getInstance(), byteArrayOutputStream); + + Assertions.assertEquals("{\"age\":18,\"id\":404,\"name\":\"dubbo\"}", + new String(byteArrayOutputStream.toByteArray())); + + + } + + @Test + void testStr() { + Object convert = DataParseUtils.stringTypeConvert(boolean.class, "true"); + + Assertions.assertEquals(Boolean.TRUE, convert); + + convert = DataParseUtils.stringTypeConvert(Boolean.class, "true"); + + Assertions.assertEquals(Boolean.TRUE, convert); + + convert = DataParseUtils.stringTypeConvert(String.class, "true"); + + Assertions.assertEquals("true", convert); + + convert = DataParseUtils.stringTypeConvert(int.class, "1"); + + Assertions.assertEquals(1, convert); + + convert = DataParseUtils.stringTypeConvert(Integer.class, "1"); + + Assertions.assertEquals(1, convert); + + + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoService.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoService.java index 265c6ce382..85e61837d8 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoService.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoService.java @@ -17,22 +17,115 @@ package org.apache.dubbo.rpc.protocol.rest; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import io.netty.handler.codec.http.DefaultFullHttpRequest; -@RestController() -@RequestMapping("/demoService") +import javax.ws.rs.Consumes; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import java.util.List; +import java.util.Map; + +@Path("/demoService") public interface DemoService { - @RequestMapping(value = "/hello", method = RequestMethod.GET) - Integer hello(@RequestParam Integer a, @RequestParam Integer b); + @GET + @Path("/hello") + Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b); - @RequestMapping(value = "/error", method = RequestMethod.GET) + @GET + @Path("/error") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + @Produces({javax.ws.rs.core.MediaType.TEXT_PLAIN}) String error(); - @RequestMapping(value = "/say", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE) - String sayHello(@RequestBody String name); + @POST + @Path("/say") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + String sayHello(String name); + + @POST + @Path("number") + Long testFormBody(@FormParam("number") Long number); + + boolean isCalled(); + + @GET + @Path("/primitive") + int primitiveInt(@QueryParam("a") int a, @QueryParam("b") int b); + + @GET + @Path("/primitiveLong") + long primitiveLong(@QueryParam("a") long a, @QueryParam("b") Long b); + + @GET + @Path("/primitiveByte") + long primitiveByte(@QueryParam("a") byte a, @QueryParam("b") Long b); + + @POST + @Path("/primitiveShort") + long primitiveShort(@QueryParam("a") short a, @QueryParam("b") Long b, int c); + + @GET + @Path("/request") + void request(DefaultFullHttpRequest defaultFullHttpRequest); + + @GET + @Path("testMapParam") + @Produces({MediaType.TEXT_PLAIN}) + @Consumes({MediaType.TEXT_PLAIN}) + String testMapParam(@QueryParam("test") Map params); + + @GET + @Path("testMapHeader") + @Produces({MediaType.TEXT_PLAIN}) + @Consumes({MediaType.TEXT_PLAIN}) + String testMapHeader(@HeaderParam("test") Map headers); + + @POST + @Path("testMapForm") + @Produces({MediaType.APPLICATION_JSON}) + @Consumes({MediaType.APPLICATION_FORM_URLENCODED}) + List testMapForm(MultivaluedMap params); + + @POST + @Path("/header") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + String header(@HeaderParam("header") String header); + + @POST + @Path("/headerInt") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + int headerInt(@HeaderParam("header") int header); + + @POST + @Path("/noStringParam") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + String noStringParam(@QueryParam("param") String param); + + @POST + @Path("/noStringHeader") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + String noStringHeader(@HeaderParam("header") String header); + + @POST + @Path("/noIntHeader") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + int noIntHeader(int header); + + @POST + @Path("/noIntParam") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + int noIntParam(int header); + + @POST + @Path("/noBodyArg") + @Consumes({MediaType.APPLICATION_JSON}) + User noBodyArg(User user); + } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoServiceImpl.java index a71fb9f45b..f5e7c2369a 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoServiceImpl.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoServiceImpl.java @@ -17,6 +17,7 @@ package org.apache.dubbo.rpc.protocol.rest; +import io.netty.handler.codec.http.DefaultFullHttpRequest; import org.apache.dubbo.rpc.RpcContext; import javax.ws.rs.Consumes; @@ -24,8 +25,12 @@ import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; +import javax.ws.rs.HeaderParam; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import java.util.List; import java.util.Map; + @Path("/demoService") public class DemoServiceImpl implements DemoService { private static Map context; @@ -40,11 +45,98 @@ public class DemoServiceImpl implements DemoService { return "Hello, " + name; } + @Override + public Long testFormBody(Long number) { + return number; + } + public boolean isCalled() { return called; } + @Override + public int primitiveInt(int a, int b) { + return a + b; + } + + @Override + public long primitiveLong(long a, Long b) { + return a + b; + } + + @Override + public long primitiveByte(byte a, Long b) { + return a + b; + } + + @Override + public long primitiveShort(short a, Long b, int c) { + return a + b; + } + + @Override + public void request(DefaultFullHttpRequest defaultFullHttpRequest) { + + } + + @Override + public String testMapParam(Map params) { + return params.get("param"); + } + + @Override + public String testMapHeader(Map headers) { + return headers.get("header"); + } + + @Override + public List testMapForm(MultivaluedMap params) { + return params.get("form"); + } + + @Override + public String header(String header) { + return header; + } + + @Override + public int headerInt(int header) { + return header; + } + + @Override + public String noStringParam(String param) { + return param; + } + + + @Override + public String noStringHeader(String header) { + return header; + } + + @POST + @Path("/noIntHeader") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + @Override + public int noIntHeader(@HeaderParam("header")int header) { + return header; + } + + @POST + @Path("/noIntParam") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + @Override + public int noIntParam(@QueryParam("header")int header) { + return header; + } + + @Override + public User noBodyArg(User user) { + return user; + } + @GET @Path("/hello") @Override @@ -58,7 +150,7 @@ public class DemoServiceImpl implements DemoService { @Path("/error") @Override public String error() { - throw new RuntimeException(); + throw new RuntimeException("test error"); } public static Map getAttachments() { diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/ExceptionMapperTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/ExceptionMapperTest.java new file mode 100644 index 0000000000..a10bbad0df --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/ExceptionMapperTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest; + +import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionHandler; +import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionMapper; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + + +public class ExceptionMapperTest { + private final ExceptionMapper exceptionMapper = new ExceptionMapper(); + + @Test + void testRegister() { + + + exceptionMapper.registerMapper(TestExceptionHandler.class); + + + Object result = exceptionMapper.exceptionToResult(new RuntimeException("test")); + + + Assertions.assertEquals("test", result); + + + } + + @Test + void testExceptionNoArgConstruct() { + + + Assertions.assertThrows(RuntimeException.class, () -> { + exceptionMapper.registerMapper(TestExceptionHandlerException.class); + + }); + + + } + + + public class TestExceptionHandler implements ExceptionHandler { + + + @Override + public Object result(RuntimeException exception) { + return exception.getMessage(); + } + } + + class TestExceptionHandlerException implements ExceptionHandler { + + + @Override + public Object result(RuntimeException exception) { + return exception.getMessage(); + } + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/HttpMessageCodecManagerTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/HttpMessageCodecManagerTest.java new file mode 100644 index 0000000000..1f514ba8e5 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/HttpMessageCodecManagerTest.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest; + +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager; +import org.apache.dubbo.rpc.protocol.rest.message.codec.XMLCodec; +import org.apache.dubbo.rpc.protocol.rest.pair.MessageCodecResultPair; +import org.apache.dubbo.rpc.protocol.rest.rest.RegistrationResult; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; + +public class HttpMessageCodecManagerTest { + + @Test + void testCodec() throws Exception { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + + RegistrationResult registrationResult = new RegistrationResult(); + registrationResult.setId(1l); + HttpMessageCodecManager.httpMessageEncode(byteArrayOutputStream, + registrationResult, null, MediaType.TEXT_XML, null); + + Object o = HttpMessageCodecManager.httpMessageDecode(byteArrayOutputStream.toByteArray(), RegistrationResult.class, MediaType.TEXT_XML); + + Assertions.assertEquals(registrationResult, o); + + byteArrayOutputStream = new ByteArrayOutputStream(); + MessageCodecResultPair messageCodecResultPair = HttpMessageCodecManager.httpMessageEncode(byteArrayOutputStream, null, null, null, RegistrationResult.class); + + MediaType mediaType = messageCodecResultPair.getMediaType(); + + Assertions.assertEquals(MediaType.APPLICATION_JSON_VALUE, mediaType); + + XMLCodec xmlCodec = new XMLCodec(); + + Assertions.assertEquals(false, xmlCodec.typeSupport(null)); + + + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java index cff2d73222..deca0a6937 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java @@ -19,6 +19,9 @@ package org.apache.dubbo.rpc.protocol.rest; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.metadata.rest.PathMatcher; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.metadata.rest.ServiceRestMetadata; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; @@ -33,17 +36,27 @@ import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; +import org.apache.dubbo.rpc.protocol.rest.annotation.metadata.MetadataResolver; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; +import org.apache.dubbo.rpc.protocol.rest.exception.DoublePathCheckException; +import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionHandler; +import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionMapper; + import org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestService; import org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestServiceImpl; +import org.apache.dubbo.rpc.protocol.rest.rest.HttpMethodService; +import org.apache.dubbo.rpc.protocol.rest.rest.HttpMethodServiceImpl; +import org.apache.dubbo.rpc.protocol.rest.rest.RestDemoForTestException; import org.hamcrest.CoreMatchers; +import org.jboss.resteasy.specimpl.MultivaluedMapImpl; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; import java.util.Arrays; +import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.remoting.Constants.SERVER_KEY; @@ -60,6 +73,7 @@ class JaxrsRestProtocolTest { private final int availablePort = NetUtils.getAvailablePort(); private final URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); private final ModuleServiceRepository repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository(); + private final ExceptionMapper exceptionMapper = new ExceptionMapper(); @AfterEach public void tearDown() { @@ -83,14 +97,28 @@ class JaxrsRestProtocolTest { String result = client.sayHello("haha"); Assertions.assertTrue(server.isCalled()); Assertions.assertEquals("Hello, haha", result); + + String header = client.header("header test"); + Assertions.assertEquals("header test", header); + + Assertions.assertEquals(1, client.headerInt(1)); invoker.destroy(); exporter.unexport(); } @Test - void testAnotherUserRestProtocol() { - URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestService"); + void testAnotherUserRestProtocolByDifferentRestClient() { + testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.OK_HTTP); + testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.APACHE_HTTP_CLIENT); + testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.URL_CONNECTION); + } + + + void testAnotherUserRestProtocol(String restClient) { + URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort() + + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestService&" + + org.apache.dubbo.remoting.Constants.CLIENT_KEY + "=" + restClient); AnotherUserRestServiceImpl server = new AnotherUserRestServiceImpl(); @@ -115,6 +143,12 @@ class JaxrsRestProtocolTest { Assertions.assertEquals(1l, client.number(1l)); + HashMap map = new HashMap<>(); + map.put("headers", "h1"); + Assertions.assertEquals("h1", client.headerMap(map)); + Assertions.assertEquals(null, client.headerMap(null)); + + invoker.destroy(); exporter.unexport(); } @@ -146,7 +180,7 @@ class JaxrsRestProtocolTest { URL url = this.registerProvider(exportUrl, server, DemoService.class); - RpcContext.getClientAttachment().setAttachment("timeout", "200"); + RpcContext.getClientAttachment().setAttachment("timeout", "20000"); Exporter exporter = protocol.export(proxy.getInvoker(server, DemoService.class, url)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, url)); @@ -174,6 +208,7 @@ class JaxrsRestProtocolTest { exporter.unexport(); } + @Disabled @Test void testServletWithoutWebConfig() { Assertions.assertThrows(RpcException.class, () -> { @@ -190,6 +225,7 @@ class JaxrsRestProtocolTest { @Test void testErrorHandler() { Assertions.assertThrows(RpcException.class, () -> { + exceptionMapper.unRegisterMapper(RuntimeException.class); DemoService server = new DemoServiceImpl(); URL url = this.registerProvider(exportUrl, server, DemoService.class); @@ -269,6 +305,7 @@ class JaxrsRestProtocolTest { exporter.unexport(); } + @Disabled @Test void testRegFail() { Assertions.assertThrows(RuntimeException.class, () -> { @@ -288,11 +325,12 @@ class JaxrsRestProtocolTest { @Test void testExceptionMapper() { + DemoService server = new DemoServiceImpl(); URL url = this.registerProvider(exportUrl, server, DemoService.class); - URL exceptionUrl = url.addParameter(EXCEPTION_MAPPER_KEY, TestExceptionMapper.class.getName()); + URL exceptionUrl = url.addParameter(EXTENSION_KEY, TestExceptionMapper.class.getName()); protocol.export(proxy.getInvoker(server, DemoService.class, exceptionUrl)); @@ -301,11 +339,225 @@ class JaxrsRestProtocolTest { Assertions.assertEquals("test-exception", referDemoService.error()); } - public static class TestExceptionMapper implements ExceptionMapper { + @Test + void testFormConsumerParser() { + DemoService server = new DemoServiceImpl(); + URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class); + + + Exporter exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); + + DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); + + + Long number = demoService.testFormBody(18l); + Assertions.assertEquals(18l, number); + + exporter.unexport(); + } + + @Test + void test404() { + Assertions.assertThrows(RpcException.class, () -> { + DemoService server = new DemoServiceImpl(); + URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class); + + + Exporter exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); + + URL referUrl = URL.valueOf("rest://127.0.0.1:" + availablePort + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.rest.RestDemoForTestException"); + + RestDemoForTestException restDemoForTestException = this.proxy.getProxy(protocol.refer(RestDemoForTestException.class, referUrl)); + + restDemoForTestException.test404(); + + exporter.unexport(); + }); + + } + + @Test + void test400() { + Assertions.assertThrows(RpcException.class, () -> { + DemoService server = new DemoServiceImpl(); + URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class); + + + Exporter exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); + + URL referUrl = URL.valueOf("rest://127.0.0.1:" + availablePort + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.rest.RestDemoForTestException"); + + RestDemoForTestException restDemoForTestException = this.proxy.getProxy(protocol.refer(RestDemoForTestException.class, referUrl)); + + restDemoForTestException.test400("abc", "edf"); + + exporter.unexport(); + }); + + } + + @Test + void testPrimitive() { + DemoService server = new DemoServiceImpl(); + + URL url = this.registerProvider(exportUrl, server, DemoService.class); + + URL nettyUrl = url.addParameter(SERVER_KEY, "netty") + .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter"); + Exporter exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); + + DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); + + Integer result = demoService.primitiveInt(1, 2); + Long resultLong = demoService.primitiveLong(1, 2l); + long resultByte = demoService.primitiveByte((byte) 1, 2l); + long resultShort = demoService.primitiveShort((short) 1, 2l, 1); + + assertThat(result, is(3)); + assertThat(resultShort, is(3l)); + assertThat(resultLong, is(3l)); + assertThat(resultByte, is(3l)); + + exporter.unexport(); + } + + + @Test + void testDoubleCheckException() { + + + Assertions.assertThrows(DoublePathCheckException.class, () -> { + + DemoService server = new DemoServiceImpl(); + + + Invoker invoker = proxy.getInvoker(server, DemoService.class, exportUrl); + + PathAndInvokerMapper pathAndInvokerMapper = new PathAndInvokerMapper(); + + ServiceRestMetadata serviceRestMetadata = MetadataResolver.resolveConsumerServiceMetadata(DemoService.class, exportUrl, ""); + + Map pathContainPathVariableToServiceMap = serviceRestMetadata.getPathUnContainPathVariableToServiceMap(); + + + pathAndInvokerMapper.addPathAndInvoker(pathContainPathVariableToServiceMap, invoker); + pathAndInvokerMapper.addPathAndInvoker(pathContainPathVariableToServiceMap, invoker); + }); + + + } + + @Test + void testMapParam() { + DemoService server = new DemoServiceImpl(); + + URL url = this.registerProvider(exportUrl, server, DemoService.class); + + URL nettyUrl = url.addParameter(SERVER_KEY, "netty") + .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter"); + Exporter exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); + + DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); + + Map params = new HashMap<>(); + params.put("param", "P1"); + ; + + + Map headers = new HashMap<>(); + headers.put("header", "H1"); + + + Assertions.assertEquals("P1", demoService.testMapParam(params)); + Assertions.assertEquals("H1", demoService.testMapHeader(headers)); + + MultivaluedMapImpl forms = new MultivaluedMapImpl<>(); + forms.put("form", Arrays.asList("F1")); + + Assertions.assertEquals(Arrays.asList("F1"), demoService.testMapForm(forms)); + exporter.unexport(); + } + + + @Test + void testNoArgParam() { + DemoService server = new DemoServiceImpl(); + + URL url = this.registerProvider(exportUrl, server, DemoService.class); + + URL nettyUrl = url.addParameter(SERVER_KEY, "netty") + .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter"); + Exporter exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); + + DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); + + + Assertions.assertEquals(null, demoService.noStringHeader(null)); + Assertions.assertEquals(null, demoService.noStringParam(null)); + Assertions.assertThrows(RpcException.class, () -> { + demoService.noIntHeader(1); + }); + + Assertions.assertThrows(RpcException.class, () -> { + demoService.noIntParam(1); + }); + + Assertions.assertEquals(null, demoService.noBodyArg(null)); + exporter.unexport(); + } + + @Test + void testToken() { + DemoService server = new DemoServiceImpl(); + + URL url = this.registerProvider(exportUrl, server, DemoService.class); + + URL nettyUrl = url.addParameter(RestConstant.TOKEN_KEY, "TOKEN"); + Exporter exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); + + DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); + + + Assertions.assertEquals("Hello, hello", demoService.sayHello("hello")); + exporter.unexport(); + } + + + @Test + void testHttpMethods() { + testHttpMethod(org.apache.dubbo.remoting.Constants.OK_HTTP); + testHttpMethod(org.apache.dubbo.remoting.Constants.APACHE_HTTP_CLIENT); + testHttpMethod(org.apache.dubbo.remoting.Constants.URL_CONNECTION); + } + + void testHttpMethod(String restClient) { + HttpMethodService server = new HttpMethodServiceImpl(); + + URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort() + + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.HttpMethodService&" + + org.apache.dubbo.remoting.Constants.CLIENT_KEY + "=" + restClient); + url = this.registerProvider(url, server, HttpMethodService.class); + Exporter exporter = protocol.export(proxy.getInvoker(server, HttpMethodService.class, url)); + + HttpMethodService demoService = this.proxy.getProxy(protocol.refer(HttpMethodService.class, url)); + + + String expect = "hello"; + Assertions.assertEquals(null, demoService.sayHelloHead()); + Assertions.assertEquals(expect, demoService.sayHelloDelete("hello")); + Assertions.assertEquals(expect, demoService.sayHelloGet("hello")); + Assertions.assertEquals(expect, demoService.sayHelloOptions("hello")); +// Assertions.assertEquals(expect, demoService.sayHelloPatch("hello")); + Assertions.assertEquals(expect, demoService.sayHelloPost("hello")); + Assertions.assertEquals(expect, demoService.sayHelloPut("hello")); + exporter.unexport(); + } + + public static class TestExceptionMapper implements ExceptionHandler { @Override - public Response toResponse(RuntimeException e) { - return Response.ok("test-exception").build(); + public String result(RuntimeException e) { + return "test-exception"; } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/MediaTypeUtilTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/MediaTypeUtilTest.java new file mode 100644 index 0000000000..bfb2e0005d --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/MediaTypeUtilTest.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest; + +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.rpc.protocol.rest.exception.UnSupportContentTypeException; +import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class MediaTypeUtilTest { + + @Test + void testException() { + + Assertions.assertThrows(UnSupportContentTypeException.class, () -> { + MediaTypeUtil.convertMediaType(null, "aaaaa"); + + }); + + + } + + @Test + void testConvertMediaType() { + MediaType mediaType = MediaTypeUtil.convertMediaType(null, new String[]{MediaType.APPLICATION_JSON_VALUE.value}); + + Assertions.assertEquals(MediaType.APPLICATION_JSON_VALUE, mediaType); + + + mediaType = MediaTypeUtil.convertMediaType(int.class, null); + + Assertions.assertEquals(MediaType.TEXT_PLAIN, mediaType); + + mediaType = MediaTypeUtil.convertMediaType(null, new String[]{MediaType.ALL_VALUE.value}); + + Assertions.assertEquals(MediaType.APPLICATION_JSON_VALUE, mediaType); + + mediaType = MediaTypeUtil.convertMediaType(String.class, new String[]{MediaType.TEXT_XML.value}); + + Assertions.assertEquals(MediaType.TEXT_XML, mediaType); + + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/NettyRequestFacadeTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/NettyRequestFacadeTest.java new file mode 100644 index 0000000000..de5f70776f --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/NettyRequestFacadeTest.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest; + +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpVersion; +import org.apache.dubbo.rpc.protocol.rest.request.NettyRequestFacade; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + +public class NettyRequestFacadeTest { + + @Test + void testMethod() { + + String uri = "/a/b?c=c&d=d"; + DefaultFullHttpRequest defaultFullHttpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri); + + defaultFullHttpRequest.headers().add("h1", "a"); + defaultFullHttpRequest.headers().add("h1", "b"); + defaultFullHttpRequest.headers().add("h2", "c"); + NettyRequestFacade nettyRequestFacade = new NettyRequestFacade(defaultFullHttpRequest, null); + + + Assertions.assertArrayEquals(new String[]{"c"}, nettyRequestFacade.getParameterValues("c")); + Enumeration parameterNames = nettyRequestFacade.getParameterNames(); + + List names = new ArrayList<>(); + while (parameterNames.hasMoreElements()) { + + names.add(parameterNames.nextElement()); + } + + Assertions.assertArrayEquals(Arrays.asList("c", "d").toArray(), names.toArray()); + + Enumeration headerNames = nettyRequestFacade.getHeaderNames(); + + List heads = new ArrayList<>(); + while (headerNames.hasMoreElements()) { + + heads.add(headerNames.nextElement()); + } + + Assertions.assertArrayEquals(Arrays.asList("h1", "h2").toArray(), heads.toArray()); + + + Assertions.assertEquals(uri, nettyRequestFacade.getRequestURI()); + + + Assertions.assertEquals("c", nettyRequestFacade.getHeader("h2")); + + Assertions.assertEquals("d", nettyRequestFacade.getParameter("d")); + + Assertions.assertEquals("/a/b", nettyRequestFacade.getPath()); + + Assertions.assertEquals(null, nettyRequestFacade.getParameterValues("e")); + + Assertions.assertArrayEquals(new String[]{"d"}, nettyRequestFacade.getParameterValues("d")); + + Enumeration h1s = nettyRequestFacade.getHeaders("h1"); + + + heads = new ArrayList<>(); + + while (h1s.hasMoreElements()) { + + heads.add(h1s.nextElement()); + } + + Assertions.assertArrayEquals(new String[]{"a","b"}, heads.toArray()); + + Map parameterMap = nettyRequestFacade.getParameterMap(); + + + Assertions.assertArrayEquals(new String[]{"c"},parameterMap.get("c")); + Assertions.assertArrayEquals(new String[]{"d"},parameterMap.get("d")); + + Assertions.assertEquals("GET",nettyRequestFacade.getMethod()); + + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/NumberUtilsTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/NumberUtilsTest.java new file mode 100644 index 0000000000..a726e38cc5 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/NumberUtilsTest.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest; + +import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils; +import org.apache.dubbo.rpc.protocol.rest.util.NumberUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; + +public class NumberUtilsTest { + void testParseNumber(String numberStr) { + int integer = NumberUtils.parseNumber(numberStr, Integer.class); + + Assertions.assertEquals(1, integer); + + integer = NumberUtils.parseNumber(numberStr, int.class); + + Assertions.assertEquals(1, integer); + + long a = NumberUtils.parseNumber(numberStr, Long.class); + + Assertions.assertEquals(1, a); + + a = NumberUtils.parseNumber(numberStr, long.class); + + Assertions.assertEquals(1, a); + + byte b = NumberUtils.parseNumber(numberStr, Byte.class); + + Assertions.assertEquals(1, b); + + b = NumberUtils.parseNumber(numberStr, byte.class); + + Assertions.assertEquals(1, b); + + short c = NumberUtils.parseNumber(numberStr, Short.class); + + Assertions.assertEquals(1, c); + + c = NumberUtils.parseNumber(numberStr, short.class); + + Assertions.assertEquals(1, c); + + BigInteger f = NumberUtils.parseNumber(numberStr, BigInteger.class); + + Assertions.assertEquals(1, f.intValue()); + + + } + + @Test + void testNumberToBytes() { + byte[] except = {49}; + byte[] bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(Integer.valueOf("1")); + + Assertions.assertArrayEquals(except, bytes); + + bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(NumberUtils.parseNumber("1", int.class)); + + Assertions.assertArrayEquals(except, bytes); + + except = new byte[]{49}; + bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(Byte.valueOf("1")); + + Assertions.assertArrayEquals(except, bytes); + + except = new byte[]{49}; + bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(Short.valueOf("1")); + + Assertions.assertArrayEquals(except, bytes); + + except = new byte[]{49}; + bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(Long.valueOf("1")); + + Assertions.assertArrayEquals(except, bytes); + + except = new byte[]{49}; + bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(BigDecimal.valueOf(1)); + + Assertions.assertArrayEquals(except, bytes); + + except = new byte[]{116, 114, 117, 101}; + bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(Boolean.TRUE); + + Assertions.assertArrayEquals(except, bytes); + + except = new byte[]{116, 114, 117, 101}; + bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(true); + + Assertions.assertArrayEquals(except, bytes); + + bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(User.getInstance()); + + except = User.getInstance().toString().getBytes(StandardCharsets.UTF_8); + Assertions.assertArrayEquals(except, bytes); + + + } + + @Test + void testNumberStr() { + testParseNumber("1"); + testParseNumber("0X0001"); + testParseNumber("0x0001"); + testParseNumber("#1"); + + } + + + @Test + void testUnHexNumber() { + String numberStr = "1"; + double e = NumberUtils.parseNumber(numberStr, Double.class); + + Assertions.assertEquals(1.0, e); + + e = NumberUtils.parseNumber(numberStr, double.class); + + Assertions.assertEquals(1.0, e); + + BigDecimal g = NumberUtils.parseNumber(numberStr, BigDecimal.class); + + Assertions.assertEquals(1, g.intValue()); + + int integer = NumberUtils.parseNumber(numberStr, int.class); + + Assertions.assertEquals(1, integer); + } + + @Test + void testNegative() { + + Integer integer = NumberUtils.parseNumber("-0X1", int.class); + Assertions.assertEquals(-1, integer); + + BigInteger bigInteger = NumberUtils.parseNumber("-0X1", BigInteger.class); + Assertions.assertEquals(-1, bigInteger.intValue()); + } + + @Test + void testException() { + + Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> { + Object abc = NumberUtils.parseNumber("abc", Object.class); + + }); + + Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> { + Object abc = NumberUtils.parseNumber(null, Object.class); + + }); + + Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> { + Object abc = NumberUtils.parseNumber("1", null); + + }); + } + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapperTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapperTest.java index b8a983c055..2749282ea7 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapperTest.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapperTest.java @@ -18,6 +18,8 @@ package org.apache.dubbo.rpc.protocol.rest; import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionHandler; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Answers; @@ -25,7 +27,8 @@ import org.mockito.internal.util.collections.Sets; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; -import javax.ws.rs.core.Response; + +import java.util.LinkedList; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.not; @@ -36,7 +39,7 @@ import static org.mockito.Mockito.mock; class RpcExceptionMapperTest { - private RpcExceptionMapper exceptionMapper; + private ExceptionHandler exceptionMapper; @BeforeEach public void setUp() { @@ -50,19 +53,60 @@ class RpcExceptionMapperTest { given(violationException.getConstraintViolations()).willReturn(Sets.newSet(violation)); RpcException rpcException = new RpcException("violation", violationException); - Response response = exceptionMapper.toResponse(rpcException); + Object response = exceptionMapper.result(rpcException); assertThat(response, not(nullValue())); - assertThat(response.getEntity(), instanceOf(ViolationReport.class)); + assertThat(response, instanceOf(ViolationReport.class)); } @Test void testNormalException() { RpcException rpcException = new RpcException(); - Response response = exceptionMapper.toResponse(rpcException); + Object response = exceptionMapper.result(rpcException); assertThat(response, not(nullValue())); - assertThat(response.getEntity(), instanceOf(String.class)); + assertThat(response, instanceOf(String.class)); + } + + @Test + void testBuildException() { + + RestConstraintViolation restConstraintViolation = new RestConstraintViolation(); + String message = "message"; + restConstraintViolation.setMessage(message); + String path = "path"; + restConstraintViolation.setPath(path); + String value = "value"; + restConstraintViolation.setValue(value); + + + Assertions.assertEquals(message, restConstraintViolation.getMessage()); + Assertions.assertEquals(path, restConstraintViolation.getPath()); + Assertions.assertEquals(value, restConstraintViolation.getValue()); + + + } + + @Test + public void testViolationReport() { + + ViolationReport violationReport = new ViolationReport(); + + + RestConstraintViolation restConstraintViolation = new RestConstraintViolation("path", "message", "value"); + + violationReport.addConstraintViolation(restConstraintViolation); + + Assertions.assertEquals(1, violationReport.getConstraintViolations().size()); + + + violationReport = new ViolationReport(); + + violationReport.setConstraintViolations(new LinkedList<>()); + + Assertions.assertEquals(0, violationReport.getConstraintViolations().size()); + + } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/SpringMvcRestProtocolTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/SpringMvcRestProtocolTest.java index fb63dea8db..0cbd1deed1 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/SpringMvcRestProtocolTest.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/SpringMvcRestProtocolTest.java @@ -32,14 +32,21 @@ import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; -import org.apache.dubbo.rpc.protocol.rest.rest.RestDemoService; -import org.apache.dubbo.rpc.protocol.rest.rest.RestDemoServiceImpl; +import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionHandler; +import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionMapper; +import org.apache.dubbo.rpc.protocol.rest.mvc.SpringDemoServiceImpl; +import org.apache.dubbo.rpc.protocol.rest.mvc.SpringRestDemoService; +import org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestService; +import org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestServiceImpl; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.springframework.util.LinkedMultiValueMap; +import java.util.Arrays; import java.util.Map; import static org.apache.dubbo.remoting.Constants.SERVER_KEY; @@ -52,64 +59,113 @@ import static org.hamcrest.MatcherAssert.assertThat; public class SpringMvcRestProtocolTest { private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rest"); private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); - private final int availablePort = NetUtils.getAvailablePort(); - private final URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); + private final static int availablePort = NetUtils.getAvailablePort(); + private final static URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.mvc.SpringRestDemoService"); + + private final ModuleServiceRepository repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository(); + private final ExceptionMapper exceptionMapper = new ExceptionMapper(); + @AfterEach public void tearDown() { protocol.destroy(); FrameworkModel.destroyAll(); } - public RestDemoService getServerImpl() { - return new RestDemoServiceImpl(); + public SpringRestDemoService getServerImpl() { + return new SpringDemoServiceImpl(); } - public Class getServerClass() { - return RestDemoService.class; + public Class getServerClass() { + return SpringRestDemoService.class; } - public Exporter getExport(URL url, RestDemoService server) { + public Exporter getExport(URL url, SpringRestDemoService server) { + url = url.addParameter(SERVER_KEY, Constants.NETTY_HTTP); + return protocol.export(proxy.getInvoker(server, getServerClass(), url)); + } + + public Exporter getExceptionHandlerExport(URL url, SpringRestDemoService server) { + url = url.addParameter(SERVER_KEY, Constants.NETTY_HTTP); + url = url.addParameter(EXTENSION_KEY, TestExceptionMapper.class.getName()); return protocol.export(proxy.getInvoker(server, getServerClass(), url)); } @Test void testRestProtocol() { - URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); + URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.mvc.SpringRestDemoService"); - RestDemoService server = getServerImpl(); + SpringRestDemoService server = getServerImpl(); url = this.registerProvider(url, server, getServerClass()); - Exporter exporter = getExport(url, server); - Invoker invoker = protocol.refer(DemoService.class, url); + Exporter exporter = getExport(url, server); + Invoker invoker = protocol.refer(SpringRestDemoService.class, url); Assertions.assertFalse(server.isCalled()); - DemoService client = proxy.getProxy(invoker); + SpringRestDemoService client = proxy.getProxy(invoker); String result = client.sayHello("haha"); Assertions.assertTrue(server.isCalled()); Assertions.assertEquals("Hello, haha", result); + + String header = client.testHeader("header"); + Assertions.assertEquals("header", header); + + String headerInt = client.testHeaderInt(1); + Assertions.assertEquals("1", headerInt); + invoker.destroy(); + exporter.unexport(); + } + + + @Test + void testAnotherUserRestProtocol() { + URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestService"); + + AnotherUserRestServiceImpl server = new AnotherUserRestServiceImpl(); + + url = this.registerProvider(url, server, SpringRestDemoService.class); + + Exporter exporter = protocol.export(proxy.getInvoker(server, AnotherUserRestService.class, url)); + Invoker invoker = protocol.refer(AnotherUserRestService.class, url); + + + AnotherUserRestService client = proxy.getProxy(invoker); + User result = client.getUser(123l); + + Assertions.assertEquals(123l, result.getId()); + + result.setName("dubbo"); + Assertions.assertEquals(123l, client.registerUser(result).getId()); + + Assertions.assertEquals("context", client.getContext()); + + byte[] bytes = {1, 2, 3, 4}; + Assertions.assertTrue(Arrays.equals(bytes, client.bytes(bytes))); + + Assertions.assertEquals(1l, client.number(1l)); + invoker.destroy(); exporter.unexport(); } @Test void testRestProtocolWithContextPath() { - RestDemoService server = getServerImpl(); + SpringRestDemoService server = getServerImpl(); Assertions.assertFalse(server.isCalled()); int port = NetUtils.getAvailablePort(); - URL url = URL.valueOf("rest://127.0.0.1:" + port + "/a/b/c?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); + URL url = URL.valueOf("rest://127.0.0.1:" + port + "/a/b/c?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.mvc.SpringRestDemoService"); - url = this.registerProvider(url, server, DemoService.class); + url = this.registerProvider(url, server, SpringRestDemoService.class); - Exporter exporter = getExport(url, server); + Exporter exporter = getExport(url, server); - url = URL.valueOf("rest://127.0.0.1:" + port + "/a/b/c/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); - Invoker invoker = protocol.refer(DemoService.class, url); - DemoService client = proxy.getProxy(invoker); + url = URL.valueOf("rest://127.0.0.1:" + port + "/a/b/c/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.mvc.SpringRestDemoService"); + Invoker invoker = protocol.refer(SpringRestDemoService.class, url); + SpringRestDemoService client = proxy.getProxy(invoker); String result = client.sayHello("haha"); Assertions.assertTrue(server.isCalled()); Assertions.assertEquals("Hello, haha", result); @@ -119,14 +175,14 @@ public class SpringMvcRestProtocolTest { @Test void testExport() { - RestDemoService server = getServerImpl(); + SpringRestDemoService server = getServerImpl(); - URL url = this.registerProvider(exportUrl, server, DemoService.class); + URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class); RpcContext.getClientAttachment().setAttachment("timeout", "200"); - Exporter exporter = getExport(url, server); + Exporter exporter = getExport(url, server); - DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, url)); + SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, url)); Integer echoString = demoService.hello(1, 2); assertThat(echoString, is(3)); @@ -136,15 +192,14 @@ public class SpringMvcRestProtocolTest { @Test void testNettyServer() { - RestDemoService server = getServerImpl(); + SpringRestDemoService server = getServerImpl(); - URL url = this.registerProvider(exportUrl, server, DemoService.class); + URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class); - URL nettyUrl = url.addParameter(SERVER_KEY, "netty"); - Exporter exporter = protocol.export(proxy.getInvoker(server, RestDemoService.class, nettyUrl)); + Exporter exporter = getExport(nettyUrl, server); - DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); + SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl)); Integer echoString = demoService.hello(10, 10); assertThat(echoString, is(20)); @@ -152,12 +207,13 @@ public class SpringMvcRestProtocolTest { exporter.unexport(); } + @Disabled @Test void testServletWithoutWebConfig() { Assertions.assertThrows(RpcException.class, () -> { - RestDemoService server = getServerImpl(); + SpringRestDemoService server = getServerImpl(); - URL url = this.registerProvider(exportUrl, server, DemoService.class); + URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class); URL servletUrl = url.addParameter(SERVER_KEY, "servlet"); @@ -168,14 +224,14 @@ public class SpringMvcRestProtocolTest { @Test void testErrorHandler() { Assertions.assertThrows(RpcException.class, () -> { - RestDemoService server = getServerImpl(); + exceptionMapper.unRegisterMapper(RuntimeException.class); + SpringRestDemoService server = getServerImpl(); - URL url = this.registerProvider(exportUrl, server, DemoService.class); + URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class); - URL nettyUrl = url.addParameter(SERVER_KEY, "netty"); - Exporter exporter = getExport(nettyUrl, server); + Exporter exporter = getExport(nettyUrl, server); - DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); + SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl)); demoService.error(); }); @@ -183,13 +239,13 @@ public class SpringMvcRestProtocolTest { @Test void testInvoke() { - RestDemoService server = getServerImpl(); + SpringRestDemoService server = getServerImpl(); - URL url = this.registerProvider(exportUrl, server, DemoService.class); + URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class); - Exporter exporter = getExport(url, server); + Exporter exporter = getExport(url, server); - RpcInvocation rpcInvocation = new RpcInvocation("hello", DemoService.class.getName(), "", new Class[]{Integer.class, Integer.class}, new Integer[]{2, 3}); + RpcInvocation rpcInvocation = new RpcInvocation("hello", SpringRestDemoService.class.getName(), "", new Class[]{Integer.class, Integer.class}, new Integer[]{2, 3}); Result result = exporter.getInvoker().invoke(rpcInvocation); assertThat(result.getValue(), CoreMatchers.is(5)); @@ -197,15 +253,13 @@ public class SpringMvcRestProtocolTest { @Test void testFilter() { - RestDemoService server = getServerImpl(); + SpringRestDemoService server = getServerImpl(); - URL url = this.registerProvider(exportUrl, server, DemoService.class); + URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class); - URL nettyUrl = url.addParameter(SERVER_KEY, "netty") - .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter"); - Exporter exporter = getExport(nettyUrl, server); + Exporter exporter = getExport(url, server); - DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); + SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, url)); Integer result = demoService.hello(1, 2); @@ -216,16 +270,16 @@ public class SpringMvcRestProtocolTest { @Test void testRpcContextFilter() { - RestDemoService server = getServerImpl(); + SpringRestDemoService server = getServerImpl(); - URL url = this.registerProvider(exportUrl, server, DemoService.class); + URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class); // use RpcContextFilter - URL nettyUrl = url.addParameter(SERVER_KEY, "netty") - .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.RpcContextFilter"); - Exporter exporter = protocol.export(proxy.getInvoker(server, RestDemoService.class, nettyUrl)); +// URL nettyUrl = url.addParameter(SERVER_KEY, "netty") +// .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.RpcContextFilter"); + Exporter exporter = getExport(nettyUrl, server); - DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); + SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl)); // make sure null and base64 encoded string can work RpcContext.getClientAttachment().setAttachment("key1", null); @@ -237,7 +291,7 @@ public class SpringMvcRestProtocolTest { assertThat(result, is(3)); - Map attachment = RestDemoServiceImpl.getAttachments(); + Map attachment = org.apache.dubbo.rpc.protocol.rest.mvc.SpringDemoServiceImpl.getAttachments(); assertThat(attachment.get("key1"), nullValue()); assertThat(attachment.get("key2"), equalTo("value")); assertThat(attachment.get("key3"), equalTo("=value")); @@ -247,15 +301,16 @@ public class SpringMvcRestProtocolTest { exporter.unexport(); } + @Disabled @Test void testRegFail() { Assertions.assertThrows(RuntimeException.class, () -> { - RestDemoService server = getServerImpl(); + SpringRestDemoService server = getServerImpl(); - URL url = this.registerProvider(exportUrl, server, DemoService.class); + URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class); URL nettyUrl = url.addParameter(EXTENSION_KEY, "com.not.existing.Filter"); - Exporter exporter = getExport(nettyUrl, server); + Exporter exporter = getExport(nettyUrl, server); }); } @@ -264,6 +319,83 @@ public class SpringMvcRestProtocolTest { assertThat(protocol.getDefaultPort(), is(80)); } + + @Test + void testExceptionMapper() { + + SpringRestDemoService server = getServerImpl(); + + URL exceptionUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class); + + Exporter exporter = getExceptionHandlerExport(exceptionUrl, server); + + + SpringRestDemoService referDemoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, exceptionUrl)); + + Assertions.assertEquals("test-exception", referDemoService.error()); + + exporter.unexport(); + } + + + @Test + void testFormConsumerParser() { + SpringRestDemoService server = getServerImpl(); + + URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class); + + + Exporter exporter = getExport(nettyUrl, server); + + SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl)); + + User user = new User(); + user.setAge(18); + user.setName("dubbo"); + user.setId(404l); + String name = demoService.testFormBody(user); + Assertions.assertEquals("dubbo", name); + + LinkedMultiValueMap forms = new LinkedMultiValueMap<>(); + forms.put("form", Arrays.asList("F1")); + + Assertions.assertEquals(Arrays.asList("F1"), demoService.testFormMapBody(forms)); + + exporter.unexport(); + } + + @Test + void testPrimitive() { + SpringRestDemoService server = getServerImpl(); + + URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class); + + + Exporter exporter = getExport(nettyUrl, server); + + SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl)); + + Integer result = demoService.primitiveInt(1, 2); + Long resultLong = demoService.primitiveLong(1, 2l); + long resultByte = demoService.primitiveByte((byte) 1, 2l); + long resultShort = demoService.primitiveShort((short) 1, 2l, 1); + + assertThat(result, is(3)); + assertThat(resultShort, is(3l)); + assertThat(resultLong, is(3l)); + assertThat(resultByte, is(3l)); + + exporter.unexport(); + } + + public static class TestExceptionMapper implements ExceptionHandler { + + @Override + public String result(RuntimeException e) { + return "test-exception"; + } + } + private URL registerProvider(URL url, Object impl, Class interfaceClass) { ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass); ProviderModel providerModel = new ProviderModel( diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/User.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/User.java index 668d8fcafd..bb16caea7d 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/User.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/User.java @@ -55,6 +55,14 @@ public class User implements Serializable { this.age = age; } + public static User getInstance() { + User user = new User(); + user.setAge(18); + user.setName("dubbo"); + user.setId(404l); + return user; + } + @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/DemoService.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/DemoService.java deleted file mode 100644 index 346ae90184..0000000000 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/DemoService.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.rpc.protocol.rest.mvc; - - -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -@RestController("/demoService") -public interface DemoService { - @RequestMapping(value = "/hello", method = RequestMethod.GET) - Integer hello(@RequestParam Integer a, @RequestParam Integer b); - - @RequestMapping(value = "/error", method = RequestMethod.GET) - String error(); - - @RequestMapping(value = "/sayHello", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE) - String sayHello(String name); -} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringDemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringDemoServiceImpl.java index 72bf284cf0..38a26a6caf 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringDemoServiceImpl.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringDemoServiceImpl.java @@ -18,10 +18,13 @@ package org.apache.dubbo.rpc.protocol.rest.mvc; import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.protocol.rest.User; +import org.springframework.util.LinkedMultiValueMap; +import java.util.List; import java.util.Map; -public class SpringDemoServiceImpl implements DemoService { +public class SpringDemoServiceImpl implements SpringRestDemoService { private static Map context; private boolean called; @@ -33,10 +36,31 @@ public class SpringDemoServiceImpl implements DemoService { } + @Override public boolean isCalled() { return called; } + @Override + public String testFormBody(User user) { + return user.getName(); + } + + @Override + public List testFormMapBody(LinkedMultiValueMap map) { + return map.get("form"); + } + + @Override + public String testHeader(String header) { + return header; + } + + @Override + public String testHeaderInt(int header) { + return String.valueOf(header); + } + @Override public Integer hello(Integer a, Integer b) { context = RpcContext.getServerAttachment().getObjectAttachments(); @@ -52,4 +76,28 @@ public class SpringDemoServiceImpl implements DemoService { public static Map getAttachments() { return context; } + + + @Override + public int primitiveInt(int a, int b) { + return a + b; + } + + @Override + public long primitiveLong(long a, Long b) { + return a + b; + } + + @Override + public long primitiveByte(byte a, Long b) { + return a + b; + } + + @Override + public long primitiveShort(short a, Long b, int c) { + return a + b; + } + + + } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringRestDemoService.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringRestDemoService.java new file mode 100644 index 0000000000..adb461bf4a --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringRestDemoService.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.mvc; + + +import org.apache.dubbo.rpc.protocol.rest.User; +import org.springframework.http.MediaType; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +@RequestMapping("/demoService") +public interface SpringRestDemoService { + @RequestMapping(value = "/hello", method = RequestMethod.GET) + Integer hello(@RequestParam Integer a, @RequestParam Integer b); + + @RequestMapping(value = "/error", method = RequestMethod.GET) + String error(); + + @RequestMapping(value = "/sayHello", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE) + String sayHello(String name); + + boolean isCalled(); + + @RequestMapping(value = "/testFormBody", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + String testFormBody(@RequestBody User user); + + @RequestMapping(value = "/testFormMapBody", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + List testFormMapBody(@RequestBody LinkedMultiValueMap map); + + @RequestMapping(value = "/testHeader", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE) + String testHeader(@RequestHeader String header); + + @RequestMapping(value = "/testHeaderInt", method = RequestMethod.GET, consumes = MediaType.TEXT_PLAIN_VALUE) + String testHeaderInt(@RequestHeader int header); + + @RequestMapping(method = RequestMethod.GET, value = "/primitive") + int primitiveInt(@RequestParam("a") int a, @RequestParam("b") int b); + + @RequestMapping(method = RequestMethod.GET, value = "/primitiveLong") + long primitiveLong(@RequestParam("a") long a, @RequestParam("b") Long b); + + @RequestMapping(method = RequestMethod.GET, value = "/primitiveByte") + long primitiveByte(@RequestParam("a") byte a, @RequestParam("b") Long b); + + + @RequestMapping(method = RequestMethod.POST, value = "/primitiveShort") + long primitiveShort(@RequestParam("a") short a, @RequestParam("b") Long b, @RequestBody int c); +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestService.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestService.java index 837fdf27c8..a2ecc36808 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestService.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestService.java @@ -20,11 +20,13 @@ import org.apache.dubbo.rpc.protocol.rest.User; import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; +import java.util.Map; @Path("u") @Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) @@ -54,4 +56,9 @@ public interface AnotherUserRestService { @Path("number") @Produces({MediaType.APPLICATION_JSON}) Long number(Long number); + + @POST + @Path("headerMap") + @Produces({MediaType.APPLICATION_JSON}) + String headerMap(@HeaderParam("headers") Map headers); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestServiceImpl.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestServiceImpl.java index 42223ebdda..7a81f071fe 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestServiceImpl.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestServiceImpl.java @@ -18,6 +18,8 @@ package org.apache.dubbo.rpc.protocol.rest.rest; import org.apache.dubbo.rpc.protocol.rest.User; +import java.util.Map; + public class AnotherUserRestServiceImpl implements AnotherUserRestService { @@ -50,4 +52,11 @@ public class AnotherUserRestServiceImpl implements AnotherUserRestService { public Long number(Long number) { return number; } + + @Override + public String headerMap(Map headers) { + return headers.get("headers"); + } + + } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/HttpMethodService.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/HttpMethodService.java new file mode 100644 index 0000000000..914da271e9 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/HttpMethodService.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.rest; + +import javax.ws.rs.*; + +@Path("/demoService") +public interface HttpMethodService { + + @POST + @Path("/sayPost") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + String sayHelloPost(@QueryParam("name") String name); + + @DELETE + @Path("/sayDelete") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + String sayHelloDelete(@QueryParam("name") String name); + + @HEAD + @Path("/sayHead") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + String sayHelloHead(); + + @GET + @Path("/sayGet") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + String sayHelloGet(@QueryParam("name") String name); + + @PUT + @Path("/sayPut") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + String sayHelloPut(@QueryParam("name") String name); + + @PATCH + @Path("/sayPatch") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + String sayHelloPatch(@QueryParam("name") String name); + + @OPTIONS + @Path("/sayOptions") + @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) + String sayHelloOptions(@QueryParam("name") String name); + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/HttpMethodServiceImpl.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/HttpMethodServiceImpl.java new file mode 100644 index 0000000000..f220d360f9 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/HttpMethodServiceImpl.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.rest; + + +public class HttpMethodServiceImpl implements HttpMethodService{ + + @Override + public String sayHelloPost(String name) { + return name; + } + + @Override + public String sayHelloDelete(String name) { + return name; + } + + @Override + public String sayHelloHead() { + return "hello"; + } + + @Override + public String sayHelloGet(String name) { + return name; + } + + @Override + public String sayHelloPut(String name) { + return name; + } + + @Override + public String sayHelloPatch(String name) { + return name; + } + + @Override + public String sayHelloOptions(String name) { + return name; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RegistrationResult.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RegistrationResult.java index 7f48b95c71..ff48474d16 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RegistrationResult.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RegistrationResult.java @@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.protocol.rest.rest; import javax.xml.bind.annotation.XmlRootElement; import java.io.Serializable; +import java.util.Objects; /** * DTO to customize the returned message @@ -41,4 +42,17 @@ public class RegistrationResult implements Serializable { public void setId(Long id) { this.id = id; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RegistrationResult that = (RegistrationResult) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoForTestException.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoForTestException.java new file mode 100644 index 0000000000..718b665ce6 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoForTestException.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.rest; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +@Path("/demoService") +public interface RestDemoForTestException { + + @POST + @Path("/noFound") + @Produces(MediaType.TEXT_PLAIN) + String test404(); + + @GET + @Consumes({MediaType.TEXT_PLAIN}) + @Path("/hello") + Integer test400(@QueryParam("a")String a,@QueryParam("b") String b); +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java index 56384d0725..4f04bd3894 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java @@ -16,11 +16,7 @@ */ package org.apache.dubbo.rpc.protocol.rest.rest; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; +import javax.ws.rs.*; import javax.ws.rs.core.MediaType; @@ -39,5 +35,11 @@ public interface RestDemoService { @Consumes({MediaType.TEXT_PLAIN}) String sayHello(String name); + @POST + @Path("number") + @Produces({MediaType.APPLICATION_FORM_URLENCODED}) + @Consumes({MediaType.APPLICATION_FORM_URLENCODED}) + Long testFormBody(@FormParam("number") Long number); + boolean isCalled(); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java index e9819e5db6..bed70b7657 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java @@ -32,6 +32,11 @@ public class RestDemoServiceImpl implements RestDemoService { return "Hello, " + name; } + @Override + public Long testFormBody(Long number) { + return number; + } + public boolean isCalled() { return called; diff --git a/dubbo-rpc/dubbo-rpc-triple/pom.xml b/dubbo-rpc/dubbo-rpc-triple/pom.xml index a285ed5baf..8a5a5fe380 100644 --- a/dubbo-rpc/dubbo-rpc-triple/pom.xml +++ b/dubbo-rpc/dubbo-rpc-triple/pom.xml @@ -98,7 +98,7 @@ kr.motd.maven os-maven-plugin - 1.6.2 + 1.7.1 initialize diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/DeadlineFuture.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/DeadlineFuture.java index f2985759fd..57e0613192 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/DeadlineFuture.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/DeadlineFuture.java @@ -20,7 +20,6 @@ package org.apache.dubbo.rpc.protocol.tri; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.resource.GlobalResourceInitializer; -import org.apache.dubbo.common.threadpool.ThreadlessExecutor; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.common.timer.Timeout; import org.apache.dubbo.common.timer.Timer; @@ -34,7 +33,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; public class DeadlineFuture extends CompletableFuture { @@ -47,7 +46,7 @@ public class DeadlineFuture extends CompletableFuture { private final long start = System.currentTimeMillis(); private final List timeoutListeners = new ArrayList<>(); private final Timeout timeoutTask; - private ExecutorService executor; + private Executor executor; private DeadlineFuture(String serviceName, String methodName, String address, int timeout) { this.serviceName = serviceName; @@ -70,13 +69,9 @@ public class DeadlineFuture extends CompletableFuture { * @return a new DeadlineFuture */ public static DeadlineFuture newFuture(String serviceName, String methodName, String address, - int timeout, ExecutorService executor) { + int timeout, Executor executor) { final DeadlineFuture future = new DeadlineFuture(serviceName, methodName, address, timeout); future.setExecutor(executor); - // ThreadlessExecutor needs to hold the waiting future in case of circuit return. - if (executor instanceof ThreadlessExecutor) { - ((ThreadlessExecutor) executor).setWaitingFuture(future); - } return future; } @@ -104,11 +99,11 @@ public class DeadlineFuture extends CompletableFuture { return timeoutListeners; } - public ExecutorService getExecutor() { + public Executor getExecutor() { return executor; } - public void setExecutor(ExecutorService executor) { + public void setExecutor(Executor executor) { this.executor = executor; } @@ -135,16 +130,6 @@ public class DeadlineFuture extends CompletableFuture { this.complete(appResponse); - // the result is returning, but the caller thread may still waiting - // to avoid endless waiting for whatever reason, notify caller thread to return. - if (executor != null && executor instanceof ThreadlessExecutor) { - ThreadlessExecutor threadlessExecutor = (ThreadlessExecutor) executor; - if (threadlessExecutor.isWaiting()) { - threadlessExecutor.notifyReturn(new IllegalStateException( - "The result has returned, but the biz thread is still waiting" - + " which is not an expected state, interrupt the thread manually by returning an exception.")); - } - } } private String getTimeoutMessage() { diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethod.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethod.java index 5316fc5452..ae647e5ca6 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethod.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethod.java @@ -23,6 +23,7 @@ import org.apache.dubbo.common.serialize.MultipleSerialization; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.config.Constants; import org.apache.dubbo.remoting.utils.UrlUtils; +import org.apache.dubbo.remoting.transport.CodecSupport; import org.apache.dubbo.rpc.model.MethodDescriptor; import org.apache.dubbo.rpc.model.PackableMethod; @@ -32,6 +33,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.ParameterizedType; +import java.util.Collection; import java.util.Iterator; import java.util.stream.Stream; @@ -55,12 +57,14 @@ public class ReflectionPackableMethod implements PackableMethod { private final boolean needWrapper; + private final Collection allSerialize; + @Override public boolean needWrapper() { return this.needWrapper; } - public ReflectionPackableMethod(MethodDescriptor method, URL url, String serializeName) { + public ReflectionPackableMethod(MethodDescriptor method, URL url, String serializeName, Collection allSerialize) { Class[] actualRequestTypes; Class actualResponseType; switch (method.getRpcType()) { @@ -98,12 +102,16 @@ public class ReflectionPackableMethod implements PackableMethod { .getExtension(url.getParameter(Constants.MULTI_SERIALIZATION_KEY, CommonConstants.DEFAULT_KEY)); + // client this.requestPack = new WrapRequestPack(serialization, url, serializeName, actualRequestTypes, singleArgument); - this.responsePack = new WrapResponsePack(serialization, url, actualResponseType); - this.requestUnpack = new WrapRequestUnpack(serialization, url, actualRequestTypes); - this.responseUnpack = new WrapResponseUnpack(serialization, url, actualResponseType); + this.responseUnpack = new WrapResponseUnpack(serialization, url, allSerialize, actualResponseType); + + // server + this.responsePack = new WrapResponsePack(serialization, url, serializeName, actualResponseType); + this.requestUnpack = new WrapRequestUnpack(serialization, url, allSerialize, actualRequestTypes); } + this.allSerialize = allSerialize; } public static ReflectionPackableMethod init(MethodDescriptor methodDescriptor, URL url) { @@ -112,8 +120,9 @@ public class ReflectionPackableMethod implements PackableMethod { if (stored != null) { return (ReflectionPackableMethod) stored; } + final Collection allSerialize = UrlUtils.allSerializations(url); ReflectionPackableMethod reflectionPackableMethod = new ReflectionPackableMethod( - methodDescriptor, url, serializeName); + methodDescriptor, url, serializeName, allSerialize); methodDescriptor.addAttribute(METHOD_ATTR_PACK, reflectionPackableMethod); return reflectionPackableMethod; } @@ -316,21 +325,23 @@ public class ReflectionPackableMethod implements PackableMethod { private final URL url; private final Class actualResponseType; - String serialize; + // wrapper request set serialize type + String requestSerialize; - private WrapResponsePack(MultipleSerialization multipleSerialization, URL url, + private WrapResponsePack(MultipleSerialization multipleSerialization, URL url, String defaultSerialize, Class actualResponseType) { this.multipleSerialization = multipleSerialization; this.url = url; this.actualResponseType = actualResponseType; + this.requestSerialize = defaultSerialize; } @Override public byte[] pack(Object obj) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); - multipleSerialization.serialize(url, serialize, actualResponseType, obj, bos); + multipleSerialization.serialize(url, requestSerialize, actualResponseType, obj, bos); return TripleCustomerProtocolWapper.TripleResponseWrapper.Builder.newBuilder() - .setSerializeType(serialize) + .setSerializeType(requestSerialize) .setType(actualResponseType.getName()) .setData(bos.toByteArray()) .build() @@ -338,24 +349,39 @@ public class ReflectionPackableMethod implements PackableMethod { } } - private static class WrapResponseUnpack implements UnPack { + private static class WrapResponseUnpack implements WrapperUnPack { private final MultipleSerialization serialization; private final URL url; private final Class returnClass; - private WrapResponseUnpack(MultipleSerialization serialization, URL url, Class returnClass) { + private final Collection allSerialize; + + + private WrapResponseUnpack(MultipleSerialization serialization, URL url, Collection allSerialize, Class returnClass) { this.serialization = serialization; this.url = url; this.returnClass = returnClass; + this.allSerialize = allSerialize; } @Override public Object unpack(byte[] data) throws IOException, ClassNotFoundException { + return unpack(data, false); + } + + + public Object unpack(byte[] data, boolean isReturnTriException) throws IOException, ClassNotFoundException { TripleCustomerProtocolWapper.TripleResponseWrapper wrapper = TripleCustomerProtocolWapper.TripleResponseWrapper .parseFrom(data); final String serializeType = convertHessianFromWrapper(wrapper.getSerializeType()); + + CodecSupport.checkSerialization(serializeType, allSerialize); + ByteArrayInputStream bais = new ByteArrayInputStream(wrapper.getData()); + if (isReturnTriException) { + return serialization.deserialize(url, serializeType, Exception.class, bais); + } return serialization.deserialize(url, serializeType, returnClass, bais); } } @@ -439,25 +465,32 @@ public class ReflectionPackableMethod implements PackableMethod { } } - private class WrapRequestUnpack implements UnPack { + private class WrapRequestUnpack implements WrapperUnPack { private final MultipleSerialization serialization; private final URL url; private final Class[] actualRequestTypes; - private WrapRequestUnpack(MultipleSerialization serialization, URL url, Class[] actualRequestTypes) { + private final Collection allSerialize; + + + private WrapRequestUnpack(MultipleSerialization serialization, URL url, Collection allSerialize, Class[] actualRequestTypes) { this.serialization = serialization; this.url = url; this.actualRequestTypes = actualRequestTypes; + this.allSerialize = allSerialize; } - @Override - public Object unpack(byte[] data) throws IOException, ClassNotFoundException { + public Object unpack(byte[] data, boolean isReturnTriException) throws IOException, ClassNotFoundException { TripleCustomerProtocolWapper.TripleRequestWrapper wrapper = TripleCustomerProtocolWapper.TripleRequestWrapper.parseFrom( data); + + String wrapperSerializeType = convertHessianFromWrapper(wrapper.getSerializeType()); + CodecSupport.checkSerialization(wrapperSerializeType, allSerialize); + Object[] ret = new Object[wrapper.getArgs().size()]; - ((WrapResponsePack) responsePack).serialize = wrapper.getSerializeType(); + ((WrapResponsePack) responsePack).requestSerialize = wrapper.getSerializeType(); for (int i = 0; i < wrapper.getArgs().size(); i++) { ByteArrayInputStream bais = new ByteArrayInputStream( wrapper.getArgs().get(i)); diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHeaderEnum.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHeaderEnum.java index f32b5244b5..7fd92f125c 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHeaderEnum.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHeaderEnum.java @@ -46,6 +46,8 @@ public enum TripleHeaderEnum { TRI_HEADER_CONVERT("tri-header-convert"), + TRI_EXCEPTION_CODE("tri-exception-code"), + ; static final Map enumMap = new HashMap<>(); diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java index 321af9d614..8daa1f600f 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java @@ -17,6 +17,15 @@ package org.apache.dubbo.rpc.protocol.tri; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.handler.codec.http2.Http2FrameCodec; +import io.netty.handler.codec.http2.Http2FrameLogger; +import io.netty.handler.codec.http2.Http2MultiplexHandler; +import io.netty.handler.codec.http2.Http2Settings; +import io.netty.handler.codec.http2.Http2StreamChannel; +import io.netty.handler.flush.FlushConsolidationHandler; +import io.netty.handler.logging.LogLevel; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.config.ConfigurationUtils; @@ -30,7 +39,6 @@ import org.apache.dubbo.remoting.api.pu.ChannelOperator; import org.apache.dubbo.remoting.api.ssl.ContextOperator; import org.apache.dubbo.rpc.HeaderFilter; import org.apache.dubbo.rpc.executor.ExecutorSupport; -import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import org.apache.dubbo.rpc.protocol.tri.transport.TripleClientHandler; @@ -40,15 +48,6 @@ import org.apache.dubbo.rpc.protocol.tri.transport.TripleServerConnectionHandler import org.apache.dubbo.rpc.protocol.tri.transport.TripleTailHandler; import org.apache.dubbo.rpc.protocol.tri.transport.TripleWriteQueue; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.http2.Http2FrameCodec; -import io.netty.handler.codec.http2.Http2FrameLogger; -import io.netty.handler.codec.http2.Http2MultiplexHandler; -import io.netty.handler.codec.http2.Http2Settings; -import io.netty.handler.codec.http2.Http2StreamChannel; -import io.netty.handler.logging.LogLevel; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -79,8 +78,6 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo private ExtensionLoader filtersLoader; private FrameworkModel frameworkModel; - private Configuration config = ConfigurationUtils.getGlobalConfiguration( - ApplicationModel.defaultModel()); public TripleHttp2Protocol() { super(new Http2ProtocolDetector()); @@ -89,12 +86,7 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo @Override public void setFrameworkModel(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; - } - - @Override - public void setApplicationModel(ApplicationModel applicationModel) { - this.config = ConfigurationUtils.getGlobalConfiguration(applicationModel); - this.filtersLoader = applicationModel.getExtensionLoader(HeaderFilter.class); + this.filtersLoader = frameworkModel.getExtensionLoader(HeaderFilter.class); } @Override @@ -104,11 +96,10 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo @Override public void configServerProtocolHandler(URL url, ChannelOperator operator) { - + Configuration config = ConfigurationUtils.getGlobalConfiguration(url.getOrDefaultApplicationModel()); final List headFilters; if (filtersLoader != null) { - headFilters = filtersLoader.getActivateExtension(url, - HEADER_FILTER_KEY); + headFilters = filtersLoader.getActivateExtension(url, HEADER_FILTER_KEY); } else { headFilters = Collections.emptyList(); } @@ -141,6 +132,7 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo }); List handlers = new ArrayList<>(); handlers.add(new ChannelHandlerPretender(codec)); + handlers.add(new ChannelHandlerPretender(new FlushConsolidationHandler(64, true))); handlers.add(new ChannelHandlerPretender(new TripleServerConnectionHandler())); handlers.add(new ChannelHandlerPretender(handler)); handlers.add(new ChannelHandlerPretender(new TripleTailHandler())); @@ -151,6 +143,7 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo @Override public void configClientPipeline(URL url, ChannelOperator operator, ContextOperator contextOperator) { + Configuration config = ConfigurationUtils.getGlobalConfiguration(url.getOrDefaultApplicationModel()); final Http2FrameCodec codec = TripleHttp2FrameCodecBuilder.forClient() .customizeConnection((connection) -> connection.remote().flowController(new TriHttp2RemoteFlowController(connection, url.getOrDefaultApplicationModel()))) .gracefulShutdownTimeoutMillis(10000) diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java index 6c11a1061a..94713b3498 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java @@ -24,16 +24,19 @@ import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.stream.StreamObserver; +import org.apache.dubbo.common.threadpool.ThreadlessExecutor; import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.CancellationContext; import org.apache.dubbo.rpc.FutureContext; import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.InvokeMode; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.TriRpcStatus; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ConsumerModel; @@ -59,6 +62,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.locks.ReentrantLock; @@ -69,6 +73,7 @@ import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAI import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_REQUEST; import static org.apache.dubbo.rpc.Constants.COMPRESSOR_KEY; import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; +import static org.apache.dubbo.rpc.model.MethodDescriptor.RpcType.UNARY; /** * TripleInvoker @@ -125,14 +130,14 @@ public class TripleInvoker extends AbstractInvoker { final MethodDescriptor methodDescriptor = serviceDescriptor.getMethod( invocation.getMethodName(), invocation.getParameterTypes()); - ClientCall call = new TripleClientCall(connectionClient, streamExecutor, + Executor callbackExecutor = isSync(methodDescriptor, invocation) ? new ThreadlessExecutor() : streamExecutor; + ClientCall call = new TripleClientCall(connectionClient, callbackExecutor, getUrl().getOrDefaultFrameworkModel(), writeQueue); - AsyncRpcResult result; try { switch (methodDescriptor.getRpcType()) { case UNARY: - result = invokeUnary(methodDescriptor, invocation, call); + result = invokeUnary(methodDescriptor, invocation, call, callbackExecutor); break; case SERVER_STREAM: result = invokeServerStream(methodDescriptor, invocation, call); @@ -160,6 +165,16 @@ public class TripleInvoker extends AbstractInvoker { } } + private static boolean isSync(MethodDescriptor methodDescriptor, Invocation invocation){ + if (!(invocation instanceof RpcInvocation)) { + return false; + } + RpcInvocation rpcInvocation = (RpcInvocation) invocation; + MethodDescriptor.RpcType rpcType = methodDescriptor.getRpcType(); + return UNARY.equals(rpcType) + && InvokeMode.SYNC.equals(rpcInvocation.getInvokeMode()); + } + AsyncRpcResult invokeServerStream(MethodDescriptor methodDescriptor, Invocation invocation, ClientCall call) { RequestMetadata request = createRequest(methodDescriptor, invocation, null); @@ -199,8 +214,7 @@ public class TripleInvoker extends AbstractInvoker { } AsyncRpcResult invokeUnary(MethodDescriptor methodDescriptor, Invocation invocation, - ClientCall call) { - ExecutorService callbackExecutor = getCallbackExecutor(getUrl(), invocation); + ClientCall call, Executor callbackExecutor) { int timeout = RpcUtils.calculateTimeout(getUrl(), invocation, invocation.getMethodName(), 3000); if (timeout <= 0) { diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/AbstractServerCall.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/AbstractServerCall.java index c385f46bd1..026031c8d4 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/AbstractServerCall.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/AbstractServerCall.java @@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.protocol.tri.call; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; @@ -79,6 +80,26 @@ public abstract class AbstractServerCall implements ServerCall, ServerStream.Lis protected PackableMethod packableMethod; protected Map requestMetadata; + private Integer exceptionCode = CommonConstants.TRI_EXCEPTION_CODE_NOT_EXISTS; + + public Integer getExceptionCode() { + return exceptionCode; + } + + public void setExceptionCode(Integer exceptionCode) { + this.exceptionCode = exceptionCode; + } + + private boolean isNeedReturnException = false; + + public boolean isNeedReturnException() { + return isNeedReturnException; + } + + public void setNeedReturnException(boolean needReturnException) { + isNeedReturnException = needReturnException; + } + AbstractServerCall(Invoker invoker, ServerStream stream, FrameworkModel frameworkModel, @@ -187,7 +208,7 @@ public abstract class AbstractServerCall implements ServerCall, ServerStream.Lis } @Override - public final void onMessage(byte[] message) { + public final void onMessage(byte[] message, boolean isReturnTriException) { ClassLoader tccl = Thread.currentThread() .getContextClassLoader(); try { @@ -272,6 +293,9 @@ public abstract class AbstractServerCall implements ServerCall, ServerStream.Lis headers.set(TripleHeaderEnum.GRPC_ENCODING.getHeader(), compressor.getMessageEncoding()); } + if (!exceptionCode.equals(CommonConstants.TRI_EXCEPTION_CODE_NOT_EXISTS)) { + headers.set(TripleHeaderEnum.TRI_EXCEPTION_CODE.getHeader(), String.valueOf(exceptionCode)); + } // send header failed will reset stream and close request observer cause no more data will be sent stream.sendHeader(headers) .addListener(f -> { @@ -323,7 +347,7 @@ public abstract class AbstractServerCall implements ServerCall, ServerStream.Lis return; } closed = true; - stream.complete(status, attachments); + stream.complete(status, attachments, isNeedReturnException, exceptionCode); } protected Long parseTimeoutToMills(String timeoutVal) { @@ -361,7 +385,7 @@ public abstract class AbstractServerCall implements ServerCall, ServerStream.Lis return; } closed = true; - stream.complete(status, null); + stream.complete(status, null, false, CommonConstants.TRI_EXCEPTION_CODE_NOT_EXISTS); LOGGER.error(PROTOCOL_FAILED_REQUEST, "", "", "Triple request error: service=" + serviceName + " method" + methodName, status.asException()); } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ClientCall.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ClientCall.java index 4936f54409..6f5b43b6b8 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ClientCall.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ClientCall.java @@ -53,7 +53,7 @@ public interface ClientCall { * @param status response status * @param trailers response trailers */ - void onClose(TriRpcStatus status, Map trailers); + void onClose(TriRpcStatus status, Map trailers, boolean isReturnTriException); } /** diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ObserverToClientCallListenerAdapter.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ObserverToClientCallListenerAdapter.java index 1c934ce970..5d427e05ee 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ObserverToClientCallListenerAdapter.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ObserverToClientCallListenerAdapter.java @@ -46,7 +46,7 @@ public class ObserverToClientCallListenerAdapter implements ClientCall.Listener } @Override - public void onClose(TriRpcStatus status, Map trailers) { + public void onClose(TriRpcStatus status, Map trailers, boolean isReturnTriException) { if (status.isOk()) { delegate.onCompleted(); } else { diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/TripleClientCall.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/TripleClientCall.java index f1d0a60f6a..59aeede63b 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/TripleClientCall.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/TripleClientCall.java @@ -68,7 +68,7 @@ public class TripleClientCall implements ClientCall, ClientStream.Listener { // stream listener start @Override - public void onMessage(byte[] message) { + public void onMessage(byte[] message, boolean isReturnTriException) { if (done) { LOGGER.warn(PROTOCOL_STREAM_LISTENER, "", "", "Received message from closed stream,connection=" + connectionClient + " service=" @@ -77,13 +77,13 @@ public class TripleClientCall implements ClientCall, ClientStream.Listener { return; } try { - final Object unpacked = requestMetadata.packableMethod.parseResponse(message); + final Object unpacked = requestMetadata.packableMethod.parseResponse(message, isReturnTriException); listener.onMessage(unpacked); } catch (Throwable t) { TriRpcStatus status = TriRpcStatus.INTERNAL.withDescription("Deserialize response failed") .withCause(t); cancelByLocal(status.asException()); - listener.onClose(status,null); + listener.onClose(status,null, false); LOGGER.error(PROTOCOL_FAILED_RESPONSE, "", "", String.format("Failed to deserialize triple response, service=%s, method=%s,connection=%s", connectionClient, requestMetadata.service, requestMetadata.method.getMethodName()), t); } @@ -98,18 +98,18 @@ public class TripleClientCall implements ClientCall, ClientStream.Listener { if (requestMetadata.cancellationContext != null) { requestMetadata.cancellationContext.cancel(status.asException()); } - onComplete(status, null, null); + onComplete(status, null, null, false); } @Override public void onComplete(TriRpcStatus status, Map attachments, - Map excludeHeaders) { + Map excludeHeaders, boolean isReturnTriException) { if (done) { return; } done = true; try { - listener.onClose(status, StreamUtils.toAttachments(attachments)); + listener.onClose(status, StreamUtils.toAttachments(attachments), isReturnTriException); } catch (Throwable t) { cancelByLocal( TriRpcStatus.INTERNAL.withDescription("Close stream error").withCause(t) @@ -191,7 +191,7 @@ public class TripleClientCall implements ClientCall, ClientStream.Listener { requestMetadata.method), t); cancelByLocal(t); listener.onClose(TriRpcStatus.INTERNAL.withDescription("Serialize request failed") - .withCause(t), null); + .withCause(t), null, false); } } // stream listener end diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/UnaryClientCallListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/UnaryClientCallListener.java index 2f00c15ac1..57dbb625f8 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/UnaryClientCallListener.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/UnaryClientCallListener.java @@ -38,11 +38,11 @@ public class UnaryClientCallListener implements ClientCall.Listener { } @Override - public void onClose(TriRpcStatus status, Map trailers) { + public void onClose(TriRpcStatus status, Map trailers, boolean isReturnTriException) { AppResponse result = new AppResponse(); result.setObjectAttachments(trailers); if (status.isOk()) { - if (appResponse instanceof Exception) { + if (isReturnTriException) { result.setException((Exception) appResponse); } else { result.setValue(appResponse); diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/UnaryServerCallListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/UnaryServerCallListener.java index 08f761a600..36011c5b3f 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/UnaryServerCallListener.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/UnaryServerCallListener.java @@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.protocol.tri.call; import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.TriRpcStatus; import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter; @@ -55,12 +56,24 @@ public class UnaryServerCallListener extends AbstractServerCallListener { @Override protected void doOnResponseHasException(Throwable t) { if (needWrapper) { - onReturn(t); + onReturnException((Exception) t); } else { super.doOnResponseHasException(t); } } + + private void onReturnException(Exception value) { + TriRpcStatus status = TriRpcStatus.getStatus(value); + int exceptionCode = status.code.code; + if (exceptionCode == TriRpcStatus.UNKNOWN.code.code) { + exceptionCode = RpcException.BIZ_EXCEPTION; + } + responseObserver.setExceptionCode(exceptionCode); + responseObserver.setNeedReturnException(true); + onReturn(value); + } + @Override public void onComplete() { invoke(); diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ServerCallToObserverAdapter.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ServerCallToObserverAdapter.java index 18bb578f01..f2ae04ac8c 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ServerCallToObserverAdapter.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ServerCallToObserverAdapter.java @@ -17,6 +17,7 @@ package org.apache.dubbo.rpc.protocol.tri.observer; +import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.CancellationContext; @@ -36,6 +37,26 @@ public class ServerCallToObserverAdapter extends CancelableStreamObserver private Map attachments; private boolean terminated = false; + private boolean isNeedReturnException = false; + + private Integer exceptionCode = CommonConstants.TRI_EXCEPTION_CODE_NOT_EXISTS; + + public Integer getExceptionCode() { + return exceptionCode; + } + + public void setExceptionCode(Integer exceptionCode) { + this.exceptionCode = exceptionCode; + } + + public boolean isNeedReturnException() { + return isNeedReturnException; + } + + public void setNeedReturnException(boolean needReturnException) { + isNeedReturnException = needReturnException; + } + public ServerCallToObserverAdapter(AbstractServerCall call, CancellationContext cancellationContext) { this.call = call; @@ -61,6 +82,8 @@ public class ServerCallToObserverAdapter extends CancelableStreamObserver throw new IllegalStateException( "Stream observer has been terminated, no more data is allowed"); } + call.setExceptionCode(exceptionCode); + call.setNeedReturnException(isNeedReturnException); call.sendMessage(data); } @@ -74,6 +97,8 @@ public class ServerCallToObserverAdapter extends CancelableStreamObserver if (isTerminated()) { return; } + call.setExceptionCode(exceptionCode); + call.setNeedReturnException(isNeedReturnException); call.close(status, attachments); setTerminated(); } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ClientStream.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ClientStream.java index 67f7320d72..42842c5fe3 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ClientStream.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ClientStream.java @@ -52,7 +52,7 @@ public interface ClientStream extends Stream { * @param reserved triple protocol reserved data */ default void onComplete(TriRpcStatus status, Map attachments, - Map reserved) { + Map reserved, boolean isReturnTriException) { onComplete(status, attachments); } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ServerStream.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ServerStream.java index f55ab58257..0b815253d0 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ServerStream.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ServerStream.java @@ -51,7 +51,7 @@ public interface ServerStream extends Stream { * @param attachments response attachments * @return a future that indicates the completion of send trailers */ - Future complete(TriRpcStatus status, Map attachments); + Future complete(TriRpcStatus status, Map attachments, boolean isNeedReturnException, int exceptionCode); /** * Send message to client diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/Stream.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/Stream.java index 01fcbf00ff..72b7aec844 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/Stream.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/Stream.java @@ -43,7 +43,7 @@ public interface Stream { * * @param message message received from remote peer */ - void onMessage(byte[] message); + void onMessage(byte[] message, boolean isReturnTriException); /** * Callback when receive cancel signal. diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtils.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtils.java index ecd1bcefa0..68807a45ec 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtils.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtils.java @@ -107,7 +107,7 @@ public class StreamUtils { convertSingleAttachment(headers, key, v); } if (!needConvertKey.isEmpty()) { - String needConvertJson = JsonUtils.getJson().toJson(needConvertKey); + String needConvertJson = JsonUtils.toJson(needConvertKey); headers.add(TripleHeaderEnum.TRI_HEADER_CONVERT.getHeader(), TriRpcStatus.encodeMessage(needConvertJson)); } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleClientStream.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleClientStream.java index d9f5dd941a..ab5816a34a 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleClientStream.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleClientStream.java @@ -17,6 +17,7 @@ package org.apache.dubbo.rpc.protocol.tri.stream; +import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.TriRpcStatus; @@ -83,6 +84,8 @@ public class TripleClientStream extends AbstractStream implements ClientStream { private boolean halfClosed; private boolean rst; + private boolean isReturnTriException = false; + // for test TripleClientStream(FrameworkModel frameworkModel, Executor executor, @@ -230,10 +233,10 @@ public class TripleClientStream extends AbstractStream implements ClientStream { void handleH2TransportError(TriRpcStatus status) { writeQueue.enqueue(CancelQueueCommand.createCommand(streamChannelFuture, Http2Error.NO_ERROR)); TripleClientStream.this.rst = true; - finishProcess(status, null); + finishProcess(status, null, false); } - void finishProcess(TriRpcStatus status, Http2Headers trailers) { + void finishProcess(TriRpcStatus status, Http2Headers trailers, boolean isReturnTriException) { final Map reserved = filterReservedHeaders(trailers); final Map attachments = headersToMap(trailers, () -> { return reserved.get(TripleHeaderEnum.TRI_HEADER_CONVERT.getHeader()); @@ -245,7 +248,7 @@ public class TripleClientStream extends AbstractStream implements ClientStream { } else { detailStatus = status; } - listener.onComplete(detailStatus, attachments, reserved); + listener.onComplete(detailStatus, attachments, reserved, isReturnTriException); } private TriRpcStatus validateHeaderStatus(Http2Headers headers) { @@ -286,6 +289,13 @@ public class TripleClientStream extends AbstractStream implements ClientStream { // todo support full payload compressor CharSequence messageEncoding = headers.get(TripleHeaderEnum.GRPC_ENCODING.getHeader()); + CharSequence triExceptionCode = headers.get(TripleHeaderEnum.TRI_EXCEPTION_CODE.getHeader()); + if (triExceptionCode != null) { + Integer triExceptionCodeNum = Integer.parseInt(triExceptionCode.toString()); + if (!(triExceptionCodeNum.equals(CommonConstants.TRI_EXCEPTION_CODE_NOT_EXISTS))) { + isReturnTriException = true; + } + } if (null != messageEncoding) { String compressorStr = messageEncoding.toString(); if (!Identity.IDENTITY.getMessageEncoding().equals(compressorStr)) { @@ -303,11 +313,11 @@ public class TripleClientStream extends AbstractStream implements ClientStream { TriDecoder.Listener listener = new TriDecoder.Listener() { @Override public void onRawMessage(byte[] data) { - TripleClientStream.this.listener.onMessage(data); + TripleClientStream.this.listener.onMessage(data, isReturnTriException); } public void close() { - finishProcess(statusFromTrailers(trailers), trailers); + finishProcess(statusFromTrailers(trailers), trailers, isReturnTriException); } }; deframer = new TriDecoder(decompressor, listener); @@ -324,7 +334,7 @@ public class TripleClientStream extends AbstractStream implements ClientStream { this.trailers = trailers; TriRpcStatus status = statusFromTrailers(trailers); if (deframer == null) { - finishProcess(status, trailers); + finishProcess(status, trailers, false); } if (deframer != null) { deframer.close(); @@ -464,7 +474,7 @@ public class TripleClientStream extends AbstractStream implements ClientStream { executor.execute(() -> { transportError = TriRpcStatus.CANCELLED .withDescription("Canceled by remote peer, errorCode=" + errorCode); - finishProcess(transportError, null); + finishProcess(transportError, null, false); }); } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleServerStream.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleServerStream.java index 5c113ba6b2..be9bfc4951 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleServerStream.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleServerStream.java @@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.protocol.tri.stream; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; @@ -157,8 +158,8 @@ public class TripleServerStream extends AbstractStream implements ServerStream { @Override - public ChannelFuture complete(TriRpcStatus status, Map attachments) { - Http2Headers trailers = getTrailers(status, attachments); + public ChannelFuture complete(TriRpcStatus status, Map attachments, boolean isNeedReturnException, int exceptionCode) { + Http2Headers trailers = getTrailers(status, attachments, isNeedReturnException, CommonConstants.TRI_EXCEPTION_CODE_NOT_EXISTS); return sendTrailers(trailers); } @@ -183,7 +184,7 @@ public class TripleServerStream extends AbstractStream implements ServerStream { }); } - private Http2Headers getTrailers(TriRpcStatus rpcStatus, Map attachments) { + private Http2Headers getTrailers(TriRpcStatus rpcStatus, Map attachments, boolean isNeedReturnException, int exceptionCode) { DefaultHttp2Headers headers = new DefaultHttp2Headers(); if (!headerSent) { headers.status(HttpResponseStatus.OK.codeAsText()); @@ -471,7 +472,7 @@ public class TripleServerStream extends AbstractStream implements ServerStream { @Override public void onRawMessage(byte[] data) { - listener.onMessage(data); + listener.onMessage(data, false); } @Override diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/AbstractH2TransportListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/AbstractH2TransportListener.java index 110e0472f5..5bcec31fde 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/AbstractH2TransportListener.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/AbstractH2TransportListener.java @@ -74,7 +74,7 @@ public abstract class AbstractH2TransportListener implements H2TransportListener } if (obj instanceof String) { String json = TriRpcStatus.decodeMessage((String) obj); - Map map = JsonUtils.getJson().toJavaObject(json, Map.class); + Map map = JsonUtils.toJavaObject(json, Map.class); for (Map.Entry entry : map.entrySet()) { Object val = attachments.remove(entry.getKey()); if (val != null) { diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleHttp2ClientResponseHandler.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleHttp2ClientResponseHandler.java index 19b35fd912..c53e04d6c5 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleHttp2ClientResponseHandler.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleHttp2ClientResponseHandler.java @@ -79,7 +79,6 @@ public final class TripleHttp2ClientResponseHandler extends @Override public void channelInactive(ChannelHandlerContext ctx) { - transportListener.cancelByRemote(Http2Error.NO_ERROR.code()); ctx.close(); } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/ExceptionUtilsTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/ExceptionUtilsTest.java index 74c0ba63a2..598da917f2 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/ExceptionUtilsTest.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/ExceptionUtilsTest.java @@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.protocol.tri; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.rpc.Exporter; @@ -90,7 +91,7 @@ class ExceptionUtilsTest { ApplicationModel applicationModel = ApplicationModel.defaultModel(); URL providerUrl = URL.valueOf( - "tri://127.0.0.1:" + availablePort + "/" + IGreeter2.class.getName()); + "tri://127.0.0.1:" + availablePort + "/" + IGreeter2.class.getName()).addParameter(CommonConstants.TIMEOUT_KEY, 10000);; ModuleServiceRepository serviceRepository = applicationModel.getDefaultModule() .getServiceRepository(); @@ -111,7 +112,7 @@ class ExceptionUtilsTest { Exporter export = protocol.export(invoker); URL consumerUrl = URL.valueOf( - "tri://127.0.0.1:" + availablePort + "/" + IGreeter2.class.getName()); + "tri://127.0.0.1:" + availablePort + "/" + IGreeter2.class.getName()).addParameter(CommonConstants.TIMEOUT_KEY, 10000); ConsumerModel consumerModel = new ConsumerModel(consumerUrl.getServiceKey(), null, serviceDescriptor, null, @@ -128,6 +129,10 @@ class ExceptionUtilsTest { } catch (IGreeterException e) { Assertions.assertEquals(EXPECT_RESPONSE_MSG, e.getMessage()); } + + Exception e = greeterProxy.echoException(REQUEST_MSG); + Assertions.assertEquals(EXPECT_RESPONSE_MSG, e.getMessage()); + export.unexport(); protocol.destroy(); // resource recycle. diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/PbUnpackTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/PbUnpackTest.java index c4bf61bd85..919cfae3a1 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/PbUnpackTest.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/PbUnpackTest.java @@ -28,10 +28,10 @@ class PbUnpackTest { @Test void unpack() throws IOException { HealthCheckRequest req = HealthCheckRequest.newBuilder() - .setService("service") - .build(); - PbUnpack unpack=new PbUnpack<>(HealthCheckRequest.class); - HealthCheckRequest obj= (HealthCheckRequest) unpack.unpack(req.toByteArray()); - Assertions.assertEquals("service",obj.getService()); + .setService("service") + .build(); + PbUnpack unpack = new PbUnpack<>(HealthCheckRequest.class); + HealthCheckRequest obj = (HealthCheckRequest) unpack.unpack(req.toByteArray()); + Assertions.assertEquals("service", obj.getService()); } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleInvokerTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleInvokerTest.java index 976d1941c7..101941423a 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleInvokerTest.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleInvokerTest.java @@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.protocol.tri; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.stream.StreamObserver; +import org.apache.dubbo.common.threadpool.ThreadlessExecutor; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient; @@ -69,7 +70,7 @@ class TripleInvokerTest { MethodDescriptor echoMethod = new ReflectionMethodDescriptor( IGreeter.class.getDeclaredMethod("echo", String.class)); Assertions.assertTrue(invoker.isAvailable()); - invoker.invokeUnary(echoMethod, invocation, call); + invoker.invokeUnary(echoMethod, invocation, call, new ThreadlessExecutor()); invoker.destroy(); Assertions.assertFalse(invoker.isAvailable()); } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/ReflectionServerCallTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/ReflectionServerCallTest.java index 7b85a34ee4..7b841e5ac4 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/ReflectionServerCallTest.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/ReflectionServerCallTest.java @@ -80,7 +80,7 @@ class ReflectionServerCallTest { Collections.emptyList(), ImmediateEventExecutor.INSTANCE); call2.onHeader(Collections.emptyMap()); - call2.onMessage(new byte[0]); + call2.onMessage(new byte[0], false); call2.onComplete(); } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/StubServerCallTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/StubServerCallTest.java index a4a0dc8f3c..98949a9237 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/StubServerCallTest.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/StubServerCallTest.java @@ -66,7 +66,7 @@ class StubServerCallTest { service, method, ImmediateEventExecutor.INSTANCE); call.onHeader(Collections.emptyMap()); - call.onMessage(new byte[0]); + call.onMessage(new byte[0], false); call.onComplete(); } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/MockClientStreamListener.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/MockClientStreamListener.java index 62a83b56a7..ac135cf0cd 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/MockClientStreamListener.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/MockClientStreamListener.java @@ -39,7 +39,7 @@ public class MockClientStreamListener implements ClientStream.Listener { } @Override - public void onMessage(byte[] message) { + public void onMessage(byte[] message, boolean isNeedReturnException) { this.message = message; } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtilsTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtilsTest.java index 068a4143f6..544332b22d 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtilsTest.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtilsTest.java @@ -72,7 +72,7 @@ class StreamUtilsTest { String jsonRaw = headers.get(TripleHeaderEnum.TRI_HEADER_CONVERT.getHeader()).toString(); String json = TriRpcStatus.decodeMessage(jsonRaw); System.out.println(jsonRaw + "---" + json); - Map upperMap = JsonUtils.getJson().toJavaObject(json, Map.class); + Map upperMap = JsonUtils.toJavaObject(json, Map.class); Assertions.assertArrayEquals("Upper".getBytes(StandardCharsets.UTF_8), upperMap.get("upper").getBytes(StandardCharsets.UTF_8)); int count = 10000; diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/IGreeter2.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/IGreeter2.java index f02b0571b7..ee0f67dcba 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/IGreeter2.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/IGreeter2.java @@ -23,4 +23,6 @@ public interface IGreeter2 { * Use request to respond */ String echo(String request) throws IGreeterException; + + Exception echoException(String request); } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/IGreeter2Impl.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/IGreeter2Impl.java index 6dca2c467b..10f35d27b1 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/IGreeter2Impl.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/IGreeter2Impl.java @@ -21,4 +21,9 @@ public class IGreeter2Impl implements IGreeter2{ public String echo(String request) throws IGreeterException { throw new IGreeterException("I am self define exception"); } + + @Override + public Exception echoException(String request) { + return new IGreeterException("I am self define exception"); + } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/resources/security/serialize.allowlist b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/security/serialize.allowlist new file mode 100644 index 0000000000..74b8dcef99 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/test/resources/security/serialize.allowlist @@ -0,0 +1,20 @@ +# +# +# 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. +# +# + +org.apache.dubbo diff --git a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectInput.java b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectInput.java index 4c3ce7c736..b63438f714 100644 --- a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectInput.java +++ b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectInput.java @@ -78,8 +78,8 @@ public interface ObjectInput extends DataInput { return (Throwable) obj; } - default Object readEvent() throws IOException, ClassNotFoundException { - return readObject(); + default String readEvent() throws IOException, ClassNotFoundException { + return readUTF(); } default Map readAttachments() throws IOException, ClassNotFoundException { diff --git a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectOutput.java b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectOutput.java index 997f781ce4..20b189fd5d 100644 --- a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectOutput.java +++ b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectOutput.java @@ -45,11 +45,11 @@ public interface ObjectOutput extends DataOutput { * restricting the content of headers / attachments to Ascii strings and uses ISO_8859_1 to encode them. * https://tools.ietf.org/html/rfc7540#section-8.1.2 */ - default void writeThrowable(Object obj) throws IOException { + default void writeThrowable(Throwable obj) throws IOException { writeObject(obj); } - default void writeEvent(Object data) throws IOException { + default void writeEvent(String data) throws IOException { writeObject(data); } diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/pom.xml b/dubbo-serialization/dubbo-serialization-fastjson2/pom.xml index 683e099d83..736bd1fc2c 100644 --- a/dubbo-serialization/dubbo-serialization-fastjson2/pom.xml +++ b/dubbo-serialization/dubbo-serialization-fastjson2/pom.xml @@ -45,5 +45,4 @@ limitations under the License. fastjson2 - diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectInput.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectInput.java index 10a261e555..6b19a110ee 100644 --- a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectInput.java +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectInput.java @@ -16,15 +16,16 @@ */ package org.apache.dubbo.common.serialize.fastjson2; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Type; - import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.utils.ClassUtils; import com.alibaba.fastjson2.JSONB; import com.alibaba.fastjson2.JSONReader; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Type; + /** * FastJson object input implementation */ @@ -112,22 +113,28 @@ public class FastJson2ObjectInput implements ObjectInput { throw new IllegalArgumentException("deserialize failed. expected read length: " + length + " but actual read: " + read); } Fastjson2SecurityManager.Handler securityFilter = fastjson2SecurityManager.getSecurityFilter(); + T result; if (securityFilter.isCheckSerializable()) { - return (T) JSONB.parseObject(bytes, Object.class, securityFilter, + result = JSONB.parseObject(bytes, cls, securityFilter, JSONReader.Feature.UseDefaultConstructorAsPossible, JSONReader.Feature.ErrorOnNoneSerializable, + JSONReader.Feature.IgnoreAutoTypeNotMatch, JSONReader.Feature.UseNativeObject, JSONReader.Feature.FieldBased); } else { - return (T) JSONB.parseObject(bytes, Object.class, securityFilter, + result = JSONB.parseObject(bytes, cls, securityFilter, JSONReader.Feature.UseDefaultConstructorAsPossible, JSONReader.Feature.UseNativeObject, + JSONReader.Feature.IgnoreAutoTypeNotMatch, JSONReader.Feature.FieldBased); } + if (result != null && cls != null && !ClassUtils.isMatch(result.getClass(), cls)) { + throw new IllegalArgumentException("deserialize failed. expected class: " + cls + " but actual class: " + result.getClass()); + } + return result; } @Override - @SuppressWarnings("unchecked") public T readObject(Class cls, Type type) throws IOException, ClassNotFoundException { updateClassLoaderIfNeed(); int length = readLength(); @@ -137,18 +144,25 @@ public class FastJson2ObjectInput implements ObjectInput { throw new IllegalArgumentException("deserialize failed. expected read length: " + length + " but actual read: " + read); } Fastjson2SecurityManager.Handler securityFilter = fastjson2SecurityManager.getSecurityFilter(); + T result; if (securityFilter.isCheckSerializable()) { - return (T) JSONB.parseObject(bytes, Object.class, securityFilter, + result = JSONB.parseObject(bytes, cls, securityFilter, JSONReader.Feature.UseDefaultConstructorAsPossible, JSONReader.Feature.ErrorOnNoneSerializable, + JSONReader.Feature.IgnoreAutoTypeNotMatch, JSONReader.Feature.UseNativeObject, JSONReader.Feature.FieldBased); } else { - return (T) JSONB.parseObject(bytes, Object.class, securityFilter, + result = JSONB.parseObject(bytes, cls, securityFilter, JSONReader.Feature.UseDefaultConstructorAsPossible, JSONReader.Feature.UseNativeObject, + JSONReader.Feature.IgnoreAutoTypeNotMatch, JSONReader.Feature.FieldBased); } + if (result != null && cls != null && !ClassUtils.isMatch(result.getClass(), cls)) { + throw new IllegalArgumentException("deserialize failed. expected class: " + cls + " but actual class: " + result.getClass()); + } + return result; } private void updateClassLoaderIfNeed() { diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/com/example/test/TestPojo.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/com/example/test/TestPojo.java new file mode 100644 index 0000000000..3c2471ba54 --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/com/example/test/TestPojo.java @@ -0,0 +1,46 @@ +/* + * 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 com.example.test; + +import java.io.Serializable; +import java.util.Objects; + +public class TestPojo implements Serializable { + private final String data; + + public TestPojo(String data) { + this.data = data; + } + + @Override + public String toString() { + throw new IllegalAccessError(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TestPojo testPojo = (TestPojo) o; + return Objects.equals(data, testPojo.data); + } + + @Override + public int hashCode() { + return Objects.hash(data); + } +} diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2SerializationTest.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2SerializationTest.java new file mode 100644 index 0000000000..447e3396aa --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2SerializationTest.java @@ -0,0 +1,583 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.fastjson2; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.serialize.ObjectOutput; +import org.apache.dubbo.common.serialize.Serialization; +import org.apache.dubbo.common.utils.SerializeCheckStatus; +import org.apache.dubbo.common.utils.SerializeSecurityManager; +import org.apache.dubbo.rpc.model.FrameworkModel; + +import com.example.test.TestPojo; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; + +public class FastJson2SerializationTest { + + @Test + void testReadString() throws IOException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("fastjson2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write string, read string + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject("hello"); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals("hello", objectInput.readUTF()); + } + + // write string, read string + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(null); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertNull(objectInput.readUTF()); + } + + // write date, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new HashMap<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, objectInput::readUTF); + } + + // write pojo, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new TrustedPojo(ThreadLocalRandom.current().nextDouble())); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertInstanceOf(String.class, objectInput.readUTF()); + } + + // write map, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new HashMap<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, objectInput::readUTF); + } + + // write list, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new LinkedList<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertInstanceOf(String.class, objectInput.readUTF()); + } + + frameworkModel.destroy(); + } + + @Test + void testReadEvent() throws IOException, ClassNotFoundException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("fastjson2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write string, read event + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject("hello"); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals("hello", objectInput.readEvent()); + } + + // write date, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new HashMap<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, objectInput::readEvent); + } + + // write pojo, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new TrustedPojo(ThreadLocalRandom.current().nextDouble())); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertInstanceOf(String.class, objectInput.readEvent()); + } + + // write map, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new HashMap<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, objectInput::readEvent); + } + + // write list, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new LinkedList<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertInstanceOf(String.class, objectInput.readEvent()); + } + + frameworkModel.destroy(); + } + + @Test + void testReadByte() throws IOException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("fastjson2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write byte, read byte + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject((byte) 11); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals((byte) 11, objectInput.readByte()); + } + + // write date, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new Date()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, objectInput::readByte); + } + + // write pojo, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new TrustedPojo(ThreadLocalRandom.current().nextDouble())); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, objectInput::readByte); + } + + // write map, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new HashMap<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, objectInput::readByte); + } + + // write list, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new LinkedList<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, objectInput::readByte); + } + + frameworkModel.destroy(); + } + + @Test + void testReadObject() throws IOException, ClassNotFoundException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("fastjson2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write pojo, read pojo + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals(trustedPojo, objectInput.readObject()); + } + + // write list, read list + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + LinkedList pojos = new LinkedList<>(); + pojos.add(trustedPojo); + + objectOutput.writeObject(pojos); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals(pojos, objectInput.readObject()); + } + + // write pojo, read pojo + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals(trustedPojo, objectInput.readObject(TrustedPojo.class)); + } + + // write list, read list + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + LinkedList pojos = new LinkedList<>(); + pojos.add(trustedPojo); + + objectOutput.writeObject(pojos); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals(pojos, objectInput.readObject(List.class)); + } + + // write list, read list + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + LinkedList pojos = new LinkedList<>(); + pojos.add(trustedPojo); + + objectOutput.writeObject(pojos); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals(pojos, objectInput.readObject(LinkedList.class)); + } + + frameworkModel.destroy(); + } + + @Test + void testReadObjectNotMatched() throws IOException, ClassNotFoundException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("fastjson2"); + frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class).setCheckStatus(SerializeCheckStatus.STRICT); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write pojo, read list failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, () -> objectInput.readObject(List.class)); + } + + // write pojo, read list failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, () -> objectInput.readObject(LinkedList.class)); + } + + // write pojo, read string failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertInstanceOf(String.class, objectInput.readObject(String.class)); + } + + // write pojo, read other failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, () -> objectInput.readObject(TrustedNotSerializable.class)); + } + + // write pojo, read same field failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, () -> objectInput.readObject(TrustedPojo2.class)); + } + + // write pojo, read map failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, () -> objectInput.readObject(Map.class)); + } + + // write list, read pojo failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + LinkedList pojos = new LinkedList<>(); + pojos.add(trustedPojo); + + objectOutput.writeObject(pojos); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, () -> objectInput.readObject(TrustedPojo.class)); + } + + // write list, read map failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + LinkedList pojos = new LinkedList<>(); + pojos.add(trustedPojo); + + objectOutput.writeObject(pojos); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, () -> objectInput.readObject(Map.class)); + } + + frameworkModel.destroy(); + } + + @Test + void testLimit1() throws IOException, ClassNotFoundException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("fastjson2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write trusted, read trusted + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals(trustedPojo, objectInput.readObject()); + + frameworkModel.destroy(); + } + + @Test + void testLimit4() throws IOException, ClassNotFoundException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + // write force untrusted, read failed + + { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("fastjson2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + TestPojo trustedPojo = new TestPojo("12345"); + + frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class).addToAllowed(trustedPojo.getClass().getName()); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + frameworkModel.destroy(); + } + + { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("fastjson2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + byte[] bytes = outputStream.toByteArray(); + frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class).setCheckStatus(SerializeCheckStatus.STRICT); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, objectInput::readObject); + frameworkModel.destroy(); + } + } + + @Test + void testLimit5() throws IOException, ClassNotFoundException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + // write force un-serializable, read failed + + { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("fastjson2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + TrustedNotSerializable trustedPojo = new TrustedNotSerializable(ThreadLocalRandom.current().nextDouble()); + + frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class).setCheckSerializable(false); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + frameworkModel.destroy(); + } + + { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("fastjson2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + byte[] bytes = outputStream.toByteArray(); + frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class).setCheckStatus(SerializeCheckStatus.STRICT); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, objectInput::readObject); + frameworkModel.destroy(); + } + } +} diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TrustedNotSerializable.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TrustedNotSerializable.java new file mode 100644 index 0000000000..3172e870fe --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TrustedNotSerializable.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.fastjson2; + +import java.util.Objects; + +public class TrustedNotSerializable { + + private final double data; + + public TrustedNotSerializable(double data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrustedNotSerializable that = (TrustedNotSerializable) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(data); + } +} diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TrustedPojo.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TrustedPojo.java new file mode 100644 index 0000000000..ae4f3db440 --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TrustedPojo.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.fastjson2; + +import java.io.Serializable; +import java.util.Objects; + +public class TrustedPojo implements Serializable { + + private final double data; + + public TrustedPojo(double data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrustedPojo that = (TrustedPojo) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(data); + } +} diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TrustedPojo2.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TrustedPojo2.java new file mode 100644 index 0000000000..3aaa74684e --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TrustedPojo2.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.fastjson2; + +import java.io.Serializable; +import java.util.Objects; + +public class TrustedPojo2 implements Serializable { + + private final double data; + + public TrustedPojo2(double data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrustedPojo2 that = (TrustedPojo2) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(data); + } +} diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TypeMatchTest.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TypeMatchTest.java new file mode 100644 index 0000000000..974f685a2a --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TypeMatchTest.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.fastjson2; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.serialize.DataInput; +import org.apache.dubbo.common.serialize.DataOutput; +import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.serialize.ObjectOutput; +import org.apache.dubbo.common.serialize.Serialization; +import org.apache.dubbo.rpc.model.FrameworkModel; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +class TypeMatchTest { + static class DataProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) throws Exception { + List datas = new LinkedList<>(); + List readMethods = new LinkedList<>(); + List writeMethods = new LinkedList<>(); + + datas.add(true); + datas.add(false); + datas.add((byte) 123); + datas.add((byte) 234); + datas.add((short) 12345); + datas.add((short) 23456); + datas.add(123456); + datas.add(234567); + datas.add(1234567L); + datas.add(2345678L); + datas.add(0.123F); + datas.add(1.234F); + datas.add(0.1234D); + datas.add(1.2345D); + datas.add("hello"); + datas.add("world"); + datas.add("hello".getBytes()); + datas.add("world".getBytes()); + + for (Method method : ObjectInput.class.getMethods()) { + if (method.getName().startsWith("read") && method.getParameterTypes().length == 0 && !method.getReturnType().equals(Object.class)) { + readMethods.add(method); + } + } + for (Method method : DataInput.class.getMethods()) { + if (method.getName().startsWith("read") && method.getParameterTypes().length == 0 && !method.getReturnType().equals(Object.class)) { + readMethods.add(method); + } + } + + for (Method method : ObjectOutput.class.getMethods()) { + if (method.getName().startsWith("write") && method.getParameterTypes().length == 1 && !method.getParameterTypes()[0].equals(Object.class)) { + writeMethods.add(method); + } + } + + for (Method method : DataOutput.class.getMethods()) { + if (method.getName().startsWith("write") && method.getParameterTypes().length == 1 && !method.getParameterTypes()[0].equals(Object.class)) { + writeMethods.add(method); + } + } + + Map, Class> primitiveWrapperTypeMap = new HashMap<>(16); + primitiveWrapperTypeMap.put(Boolean.class, boolean.class); + primitiveWrapperTypeMap.put(Byte.class, byte.class); + primitiveWrapperTypeMap.put(Character.class, char.class); + primitiveWrapperTypeMap.put(Double.class, double.class); + primitiveWrapperTypeMap.put(Float.class, float.class); + primitiveWrapperTypeMap.put(Integer.class, int.class); + primitiveWrapperTypeMap.put(Long.class, long.class); + primitiveWrapperTypeMap.put(Short.class, short.class); + primitiveWrapperTypeMap.put(Void.class, void.class); + + List argumentsList = new LinkedList<>(); + for (Object data : datas) { + for (Method input : readMethods) { + for (Method output : writeMethods) { + if (output.getParameterTypes()[0].isAssignableFrom(data.getClass())) { + argumentsList.add(Arguments.arguments(data, input, output)); + } + if (primitiveWrapperTypeMap.containsKey(data.getClass()) && + output.getParameterTypes()[0].isAssignableFrom(primitiveWrapperTypeMap.get(data.getClass()))) { + argumentsList.add(Arguments.arguments(data, input, output)); + } + } + } + } + + return argumentsList.stream(); + } + } + + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + void test(Object data, Method input, Method output) throws Exception { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("fastjson2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + output.invoke(objectOutput, data); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + if (output.getParameterTypes()[0].equals(input.getReturnType())) { + Object result = input.invoke(objectInput); + if (data.getClass().isArray()) { + Assertions.assertArrayEquals((byte[]) data, (byte[]) result); + } else { + Assertions.assertEquals(data, result); + } + } else { + try { + Object result = input.invoke(objectInput); + if (data.getClass().isArray()) { + Assertions.assertNotEquals(data.getClass(), result.getClass()); + } else { + Assertions.assertNotEquals(data, result); + } + } catch (Exception e) { + // ignore + } + } + frameworkModel.destroy(); + } +} diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/test/resources/security/serialize.allowlist b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/resources/security/serialize.allowlist new file mode 100644 index 0000000000..922a941869 --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/test/resources/security/serialize.allowlist @@ -0,0 +1,20 @@ +# +# +# 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. +# +# + +org.apache.dubbo.common.serialize.fastjson2 diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2FactoryManager.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2FactoryManager.java index 512f67115f..60f9dfe2fe 100644 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2FactoryManager.java +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2FactoryManager.java @@ -20,6 +20,8 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.dubbo.common.utils.DefaultSerializeClassChecker; +import org.apache.dubbo.common.utils.SerializeCheckStatus; +import org.apache.dubbo.common.utils.SerializeSecurityManager; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.model.FrameworkModel; @@ -33,9 +35,11 @@ public class Hessian2FactoryManager { private volatile SerializerFactory SYSTEM_SERIALIZER_FACTORY; private final Map CL_2_SERIALIZER_FACTORY = new ConcurrentHashMap<>(); + private final SerializeSecurityManager serializeSecurityManager; private final DefaultSerializeClassChecker defaultSerializeClassChecker; public Hessian2FactoryManager(FrameworkModel frameworkModel) { + serializeSecurityManager = frameworkModel.getBeanFactory().getOrRegisterBean(SerializeSecurityManager.class); defaultSerializeClassChecker = frameworkModel.getBeanFactory().getOrRegisterBean(DefaultSerializeClassChecker.class); } @@ -89,14 +93,17 @@ public class Hessian2FactoryManager { if (StringUtils.isNotEmpty(allowPattern)) { for (String pattern : allowPattern.split(";")) { serializerFactory.getClassFactory().allow(pattern); + serializeSecurityManager.addToAlwaysAllowed(pattern); } } + serializeSecurityManager.setCheckStatus(SerializeCheckStatus.STRICT); } else { serializerFactory.getClassFactory().setWhitelist(false); String denyPattern = System.getProperty(DENY); if (StringUtils.isNotEmpty(denyPattern)) { for (String pattern : denyPattern.split(";")) { serializerFactory.getClassFactory().deny(pattern); + serializeSecurityManager.addToDisAllowed(pattern); } } } diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ScopeModelInitializer.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ScopeModelInitializer.java index 578850905c..a86b39bdb8 100644 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ScopeModelInitializer.java +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ScopeModelInitializer.java @@ -27,6 +27,8 @@ public class Hessian2ScopeModelInitializer implements ScopeModelInitializer { public void initializeFrameworkModel(FrameworkModel frameworkModel) { ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); beanFactory.registerBean(Hessian2FactoryManager.class); + + frameworkModel.addClassLoaderListener(new Hessian2ClassLoaderListener()); } @Override diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializerFactory.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializerFactory.java index 099c5e9766..0d886b9d39 100644 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializerFactory.java +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializerFactory.java @@ -52,7 +52,7 @@ public class Hessian2SerializerFactory extends SerializerFactory { } if (!Serializable.class.isAssignableFrom(cl) - && (!isAllowNonSerializable() || !defaultSerializeClassChecker.isCheckSerializable())) { + && (!isAllowNonSerializable() || defaultSerializeClassChecker.isCheckSerializable())) { throw new IllegalStateException("Serialized class " + cl.getName() + " must implement java.io.Serializable"); } diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/com/example/test/TestPojo.java b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/com/example/test/TestPojo.java new file mode 100644 index 0000000000..3c2471ba54 --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/com/example/test/TestPojo.java @@ -0,0 +1,46 @@ +/* + * 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 com.example.test; + +import java.io.Serializable; +import java.util.Objects; + +public class TestPojo implements Serializable { + private final String data; + + public TestPojo(String data) { + this.data = data; + } + + @Override + public String toString() { + throw new IllegalAccessError(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TestPojo testPojo = (TestPojo) o; + return Objects.equals(data, testPojo.data); + } + + @Override + public int hashCode() { + return Objects.hash(data); + } +} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializationTest.java b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializationTest.java new file mode 100644 index 0000000000..b43e0a8c80 --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializationTest.java @@ -0,0 +1,615 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.hessian2; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.serialize.ObjectOutput; +import org.apache.dubbo.common.serialize.Serialization; +import org.apache.dubbo.common.utils.SerializeCheckStatus; +import org.apache.dubbo.common.utils.SerializeSecurityManager; +import org.apache.dubbo.rpc.model.FrameworkModel; + +import com.example.test.TestPojo; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; + +class Hessian2SerializationTest { + @Test + void testReadString() throws IOException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write string, read string + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject("hello"); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals("hello", objectInput.readUTF()); + } + + // write string, read string + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(null); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertNull(objectInput.readUTF()); + } + + // write date, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new Date()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(IOException.class, objectInput::readUTF); + } + + // write pojo, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new TrustedPojo(ThreadLocalRandom.current().nextDouble())); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(IOException.class, objectInput::readUTF); + } + + // write map, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new HashMap<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(IOException.class, objectInput::readUTF); + } + + // write list, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new LinkedList<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(IOException.class, objectInput::readUTF); + } + + frameworkModel.destroy(); + } + + @Test + void testReadEvent() throws IOException, ClassNotFoundException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write string, read event + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject("hello"); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals("hello", objectInput.readEvent()); + } + + // write date, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new Date()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(IOException.class, objectInput::readEvent); + } + + // write pojo, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new TrustedPojo(ThreadLocalRandom.current().nextDouble())); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(IOException.class, objectInput::readEvent); + } + + // write map, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new HashMap<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(IOException.class, objectInput::readEvent); + } + + // write list, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new LinkedList<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(IOException.class, objectInput::readEvent); + } + + frameworkModel.destroy(); + } + + @Test + void testReadByte() throws IOException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write byte, read byte + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject((byte) 11); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals((byte) 11, objectInput.readByte()); + } + + // write date, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new Date()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(IOException.class, objectInput::readByte); + } + + // write pojo, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new TrustedPojo(ThreadLocalRandom.current().nextDouble())); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(IOException.class, objectInput::readByte); + } + + // write map, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new HashMap<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(IOException.class, objectInput::readByte); + } + + // write list, read failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(new LinkedList<>()); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(IOException.class, objectInput::readByte); + } + + frameworkModel.destroy(); + } + + @Test + void testReadObject() throws IOException, ClassNotFoundException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write pojo, read pojo + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals(trustedPojo, objectInput.readObject()); + } + + // write list, read list + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + LinkedList pojos = new LinkedList<>(); + pojos.add(trustedPojo); + + objectOutput.writeObject(pojos); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals(pojos, objectInput.readObject()); + } + + // write pojo, read pojo + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals(trustedPojo, objectInput.readObject(TrustedPojo.class)); + } + + // write list, read list + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + LinkedList pojos = new LinkedList<>(); + pojos.add(trustedPojo); + + objectOutput.writeObject(pojos); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals(pojos, objectInput.readObject(List.class)); + } + + // write list, read list + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + LinkedList pojos = new LinkedList<>(); + pojos.add(trustedPojo); + + objectOutput.writeObject(pojos); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals(pojos, objectInput.readObject(LinkedList.class)); + } + + frameworkModel.destroy(); + } + + @Test + void testReadObjectNotMatched() throws IOException, ClassNotFoundException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write pojo, read list failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, () -> objectInput.readObject(List.class)); + } + + // write pojo, read list failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, () -> objectInput.readObject(LinkedList.class)); + } + + // write pojo, read string failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, () -> objectInput.readObject(String.class)); + } + + // write pojo, read other failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, () -> objectInput.readObject(TrustedNotSerializable.class)); + } + + // write pojo, read same field failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertInstanceOf(TrustedPojo2.class, objectInput.readObject(TrustedPojo2.class)); + } + + // write pojo, read map failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertInstanceOf(Map.class, objectInput.readObject(Map.class)); + } + + // write list, read pojo failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + LinkedList pojos = new LinkedList<>(); + pojos.add(trustedPojo); + + objectOutput.writeObject(pojos); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, () -> objectInput.readObject(TrustedPojo.class)); + } + + // write list, read map failed + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + LinkedList pojos = new LinkedList<>(); + pojos.add(trustedPojo); + + objectOutput.writeObject(pojos); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertThrows(RuntimeException.class, () -> objectInput.readObject(Map.class)); + } + + frameworkModel.destroy(); + } + + @Test + void testLimit1() throws IOException, ClassNotFoundException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write trusted, read trusted + TrustedPojo trustedPojo = new TrustedPojo(ThreadLocalRandom.current().nextDouble()); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals(trustedPojo, objectInput.readObject()); + + frameworkModel.destroy(); + } + + @Test + void testLimit2() throws IOException, ClassNotFoundException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class).setCheckStatus(SerializeCheckStatus.STRICT); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write untrusted failed + TestPojo trustedPojo = new TestPojo("12345"); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + Assertions.assertThrows(IllegalArgumentException.class, () -> objectOutput.writeObject(trustedPojo)); + + frameworkModel.destroy(); + } + + @Test + void testLimit3() throws IOException, ClassNotFoundException { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + // write un-serializable failed + TrustedNotSerializable trustedPojo = new TrustedNotSerializable(ThreadLocalRandom.current().nextDouble()); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + Assertions.assertThrows(IllegalArgumentException.class, () -> objectOutput.writeObject(trustedPojo)); + + frameworkModel.destroy(); + } + + @Test + void testLimit4() throws IOException, ClassNotFoundException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + // write force untrusted, read failed + + { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + TestPojo trustedPojo = new TestPojo("12345"); + + frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class).addToAllowed(trustedPojo.getClass().getName()); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + frameworkModel.destroy(); + } + + { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + byte[] bytes = outputStream.toByteArray(); + frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class).setCheckStatus(SerializeCheckStatus.STRICT); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertInstanceOf(Map.class, objectInput.readObject()); + frameworkModel.destroy(); + } + } + + @Test + void testLimit5() throws IOException, ClassNotFoundException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + // write force un-serializable, read failed + + { + System.setProperty("dubbo.hessian.allowNonSerializable", "true"); + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + TrustedNotSerializable trustedPojo = new TrustedNotSerializable(ThreadLocalRandom.current().nextDouble()); + + frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class).setCheckSerializable(false); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(trustedPojo); + objectOutput.flushBuffer(); + + frameworkModel.destroy(); + System.clearProperty("dubbo.hessian.allowNonSerializable"); + } + + { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertInstanceOf(Map.class, objectInput.readObject()); + frameworkModel.destroy(); + } + } +} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TrustedNotSerializable.java b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TrustedNotSerializable.java new file mode 100644 index 0000000000..dea99604e8 --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TrustedNotSerializable.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.hessian2; + +import java.util.Objects; + +public class TrustedNotSerializable { + + private final double data; + + public TrustedNotSerializable(double data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrustedNotSerializable that = (TrustedNotSerializable) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(data); + } +} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TrustedPojo.java b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TrustedPojo.java new file mode 100644 index 0000000000..14eabaa853 --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TrustedPojo.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.hessian2; + +import java.io.Serializable; +import java.util.Objects; + +public class TrustedPojo implements Serializable { + + private final double data; + + public TrustedPojo(double data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrustedPojo that = (TrustedPojo) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(data); + } +} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TrustedPojo2.java b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TrustedPojo2.java new file mode 100644 index 0000000000..db499e49f3 --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TrustedPojo2.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.hessian2; + +import java.io.Serializable; +import java.util.Objects; + +public class TrustedPojo2 implements Serializable { + + private final double data; + + public TrustedPojo2(double data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrustedPojo2 that = (TrustedPojo2) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(data); + } +} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TypeMatchTest.java b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TypeMatchTest.java new file mode 100644 index 0000000000..b155981be7 --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TypeMatchTest.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.hessian2; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.serialize.DataInput; +import org.apache.dubbo.common.serialize.DataOutput; +import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.serialize.ObjectOutput; +import org.apache.dubbo.common.serialize.Serialization; +import org.apache.dubbo.common.utils.ClassUtils; +import org.apache.dubbo.rpc.model.FrameworkModel; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.jupiter.params.provider.ArgumentsSources; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +class TypeMatchTest { + static class DataProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) throws Exception { + List datas = new LinkedList<>(); + List readMethods = new LinkedList<>(); + List writeMethods = new LinkedList<>(); + + datas.add(true); + datas.add(false); + datas.add((byte) 123); + datas.add((byte) 234); + datas.add((short) 12345); + datas.add((short) 23456); + datas.add(123456); + datas.add(234567); + datas.add(1234567L); + datas.add(2345678L); + datas.add(0.123F); + datas.add(1.234F); + datas.add(0.1234D); + datas.add(1.2345D); + datas.add("hello"); + datas.add("world"); + datas.add("hello".getBytes()); + datas.add("world".getBytes()); + + for (Method method : ObjectInput.class.getMethods()) { + if (method.getName().startsWith("read") && method.getParameterTypes().length == 0 && !method.getReturnType().equals(Object.class)) { + readMethods.add(method); + } + } + for (Method method : DataInput.class.getMethods()) { + if (method.getName().startsWith("read") && method.getParameterTypes().length == 0 && !method.getReturnType().equals(Object.class)) { + readMethods.add(method); + } + } + + for (Method method : ObjectOutput.class.getMethods()) { + if (method.getName().startsWith("write") && method.getParameterTypes().length == 1 && !method.getParameterTypes()[0].equals(Object.class)) { + writeMethods.add(method); + } + } + + for (Method method : DataOutput.class.getMethods()) { + if (method.getName().startsWith("write") && method.getParameterTypes().length == 1 && !method.getParameterTypes()[0].equals(Object.class)) { + writeMethods.add(method); + } + } + + Map, Class> primitiveWrapperTypeMap = new HashMap<>(16); + primitiveWrapperTypeMap.put(Boolean.class, boolean.class); + primitiveWrapperTypeMap.put(Byte.class, byte.class); + primitiveWrapperTypeMap.put(Character.class, char.class); + primitiveWrapperTypeMap.put(Double.class, double.class); + primitiveWrapperTypeMap.put(Float.class, float.class); + primitiveWrapperTypeMap.put(Integer.class, int.class); + primitiveWrapperTypeMap.put(Long.class, long.class); + primitiveWrapperTypeMap.put(Short.class, short.class); + primitiveWrapperTypeMap.put(Void.class, void.class); + + List argumentsList = new LinkedList<>(); + for (Object data : datas) { + for (Method input : readMethods) { + for (Method output : writeMethods) { + if (output.getParameterTypes()[0].isAssignableFrom(data.getClass())) { + argumentsList.add(Arguments.arguments(data, input, output)); + } + if (primitiveWrapperTypeMap.containsKey(data.getClass()) && + output.getParameterTypes()[0].isAssignableFrom(primitiveWrapperTypeMap.get(data.getClass()))) { + argumentsList.add(Arguments.arguments(data, input, output)); + } + } + } + } + + return argumentsList.stream(); + } + } + + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + void test(Object data, Method input, Method output) throws Exception { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + output.invoke(objectOutput, data); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + if (output.getParameterTypes()[0].equals(input.getReturnType())) { + Object result = input.invoke(objectInput); + if (data.getClass().isArray()) { + Assertions.assertArrayEquals((byte[]) data, (byte[]) result); + } else { + Assertions.assertEquals(data, result); + } + } else { + try { + Object result = input.invoke(objectInput); + if (data.getClass().isArray()) { + Assertions.assertNotEquals(data.getClass(), result.getClass()); + } else { + Assertions.assertNotEquals(data, result); + } + } catch (Exception e) { + // ignore + } + } + frameworkModel.destroy(); + } +} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/test/resources/security/serialize.allowlist b/dubbo-serialization/dubbo-serialization-hessian2/src/test/resources/security/serialize.allowlist new file mode 100644 index 0000000000..bc5ad95eb0 --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/test/resources/security/serialize.allowlist @@ -0,0 +1,20 @@ +# +# +# 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. +# +# + +org.apache.dubbo.common.serialize.hessian2 diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboConfigurationProperties.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboConfigurationProperties.java index 165a90cf30..ff8fdda5a8 100644 --- a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboConfigurationProperties.java +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboConfigurationProperties.java @@ -26,6 +26,7 @@ import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.context.ConfigMode; import org.apache.dubbo.config.spring.ConfigCenterBean; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; @@ -88,6 +89,9 @@ public class DubboConfigurationProperties { @NestedConfigurationProperty private MetricsConfig metrics = new MetricsConfig(); + @NestedConfigurationProperty + private TracingConfig tracing = new TracingConfig(); + // Multiple Config Bindings private Map modules = new LinkedHashMap<>(); @@ -108,6 +112,8 @@ public class DubboConfigurationProperties { private Map metricses = new LinkedHashMap<>(); + private Map tracings = new LinkedHashMap<>(); + public Config getConfig() { return config; } @@ -204,6 +210,14 @@ public class DubboConfigurationProperties { this.metrics = metrics; } + public TracingConfig getTracing() { + return tracing; + } + + public void setTracing(TracingConfig tracing) { + this.tracing = tracing; + } + public Map getModules() { return modules; } @@ -276,6 +290,14 @@ public class DubboConfigurationProperties { this.metricses = metricses; } + public Map getTracings() { + return tracings; + } + + public void setTracings(Map tracings) { + this.tracings = tracings; + } + static class Config { /** diff --git a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/pom.xml b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/pom.xml index 7e4b5978f5..ce72ee602b 100644 --- a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/pom.xml +++ b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/pom.xml @@ -29,7 +29,7 @@ dubbo-spring-boot-observability-starter - 1.10.5 + 1.10.6 1.0.3 @@ -65,6 +65,18 @@ io.micrometer micrometer-core + + io.micrometer + micrometer-registry-prometheus + + + io.prometheus + simpleclient_pushgateway + + + com.tdunning + t-digest + org.springframework.boot spring-boot-autoconfigure @@ -75,12 +87,6 @@ spring-boot-configuration-processor true - - org.apache.dubbo - dubbo-common - ${project.version} - true - io.micrometer micrometer-tracing-bridge-otel @@ -93,9 +99,15 @@ org.apache.dubbo - dubbo-metrics-default + dubbo-spring-boot-starter ${project.version} + + org.apache.dubbo + dubbo-common + ${project.version} + true + diff --git a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/DubboMicrometerTracingAutoConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/DubboMicrometerTracingAutoConfiguration.java index a1f44a1867..ed2553c664 100644 --- a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/DubboMicrometerTracingAutoConfiguration.java +++ b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/DubboMicrometerTracingAutoConfiguration.java @@ -16,11 +16,6 @@ */ package org.apache.dubbo.spring.boot.observability.autoconfigure; -import io.micrometer.tracing.Tracer; -import io.micrometer.tracing.handler.DefaultTracingObservationHandler; -import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler; -import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler; -import io.micrometer.tracing.propagation.Propagator; import org.apache.dubbo.spring.boot.observability.annotation.ConditionalOnDubboTracingEnable; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -33,46 +28,46 @@ import org.springframework.core.annotation.Order; * copy from {@link org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration} * this class is available starting from Boot 3.0. It's not available if you're using Boot < 3.0 */ -@ConditionalOnClass(Tracer.class) @ConditionalOnDubboTracingEnable +@ConditionalOnClass(name = {"io.micrometer.observation.Observation","io.micrometer.tracing.Tracer","io.micrometer.tracing.propagation.Propagator"}) @AutoConfigureAfter(name = "org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration") public class DubboMicrometerTracingAutoConfiguration { /** * {@code @Order} value of - * {@link #propagatingReceiverTracingObservationHandler(Tracer, Propagator)}. + * {@link #propagatingReceiverTracingObservationHandler(io.micrometer.tracing.Tracer, io.micrometer.tracing.propagation.Propagator )}. */ public static final int RECEIVER_TRACING_OBSERVATION_HANDLER_ORDER = 1000; /** * {@code @Order} value of - * {@link #propagatingSenderTracingObservationHandler(Tracer, Propagator)}. + * {@link #propagatingSenderTracingObservationHandler(io.micrometer.tracing.Tracer, io.micrometer.tracing.propagation.Propagator )}. */ public static final int SENDER_TRACING_OBSERVATION_HANDLER_ORDER = 2000; @Bean @ConditionalOnMissingBean - @ConditionalOnBean(Tracer.class) - public DefaultTracingObservationHandler defaultTracingObservationHandler(Tracer tracer) { - return new DefaultTracingObservationHandler(tracer); + @ConditionalOnBean(io.micrometer.tracing.Tracer.class) + public io.micrometer.tracing.handler.DefaultTracingObservationHandler defaultTracingObservationHandler(io.micrometer.tracing.Tracer tracer) { + return new io.micrometer.tracing.handler.DefaultTracingObservationHandler(tracer); } @Bean @ConditionalOnMissingBean - @ConditionalOnBean({Tracer.class, Propagator.class}) + @ConditionalOnBean({io.micrometer.tracing.Tracer.class, io.micrometer.tracing.propagation.Propagator.class}) @Order(SENDER_TRACING_OBSERVATION_HANDLER_ORDER) - public PropagatingSenderTracingObservationHandler propagatingSenderTracingObservationHandler(Tracer tracer, - Propagator propagator) { - return new PropagatingSenderTracingObservationHandler<>(tracer, propagator); + public io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler propagatingSenderTracingObservationHandler(io.micrometer.tracing.Tracer tracer, + io.micrometer.tracing.propagation.Propagator propagator) { + return new io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler<>(tracer, propagator); } @Bean @ConditionalOnMissingBean - @ConditionalOnBean({Tracer.class, Propagator.class}) + @ConditionalOnBean({io.micrometer.tracing.Tracer.class, io.micrometer.tracing.propagation.Propagator.class}) @Order(RECEIVER_TRACING_OBSERVATION_HANDLER_ORDER) - public PropagatingReceiverTracingObservationHandler propagatingReceiverTracingObservationHandler(Tracer tracer, - Propagator propagator) { - return new PropagatingReceiverTracingObservationHandler<>(tracer, propagator); + public io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler propagatingReceiverTracingObservationHandler(io.micrometer.tracing.Tracer tracer, + io.micrometer.tracing.propagation.Propagator propagator) { + return new io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler<>(tracer, propagator); } } diff --git a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/DubboObservationAutoConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/DubboObservationAutoConfiguration.java index 6b98ba9090..f87c7787a8 100644 --- a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/DubboObservationAutoConfiguration.java +++ b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/DubboObservationAutoConfiguration.java @@ -17,23 +17,20 @@ package org.apache.dubbo.spring.boot.observability.autoconfigure; import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler; -import io.micrometer.core.instrument.observation.MeterObservationHandler; -import io.micrometer.observation.Observation; -import io.micrometer.observation.ObservationHandler; -import io.micrometer.observation.ObservationRegistry; -import io.micrometer.tracing.Tracer; -import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler; -import io.micrometer.tracing.handler.TracingObservationHandler; +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.qos.protocol.QosProtocolWrapper; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.spring.boot.observability.annotation.ConditionalOnDubboTracingEnable; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.SmartInitializingSingleton; + import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -50,7 +47,10 @@ import java.util.Arrays; */ @AutoConfiguration(after = DubboMicrometerTracingAutoConfiguration.class, afterName = "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration") @ConditionalOnDubboTracingEnable +@ConditionalOnClass(name = {"io.micrometer.observation.Observation", "io.micrometer.tracing.Tracer"}) public class DubboObservationAutoConfiguration implements BeanFactoryAware, SmartInitializingSingleton { + private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(QosProtocolWrapper.class); + public DubboObservationAutoConfiguration(ApplicationModel applicationModel) { this.applicationModel = applicationModel; @@ -62,14 +62,16 @@ public class DubboObservationAutoConfiguration implements BeanFactoryAware, Smar @Bean @ConditionalOnMissingBean - ObservationRegistry observationRegistry() { - return ObservationRegistry.create(); + @ConditionalOnClass(name = "io.micrometer.observation.ObservationRegistry") + io.micrometer.observation.ObservationRegistry observationRegistry() { + return io.micrometer.observation.ObservationRegistry.create(); } @Bean @ConditionalOnMissingBean(type = "org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryPostProcessor") + @ConditionalOnClass(name = "io.micrometer.observation.ObservationHandler") public ObservationRegistryPostProcessor dubboObservationRegistryPostProcessor(ObjectProvider observationHandlerGrouping, - ObjectProvider> observationHandlers) { + ObjectProvider> observationHandlers) { return new ObservationRegistryPostProcessor(observationHandlerGrouping, observationHandlers); } @@ -80,7 +82,13 @@ public class DubboObservationAutoConfiguration implements BeanFactoryAware, Smar @Override public void afterSingletonsInstantiated() { - applicationModel.getBeanFactory().registerBean(beanFactory.getBean(ObservationRegistry.class)); + try { + applicationModel.getBeanFactory().registerBean(beanFactory.getBean(io.micrometer.observation.ObservationRegistry.class)); + io.micrometer.tracing.Tracer bean = beanFactory.getBean(io.micrometer.tracing.Tracer.class); + applicationModel.getBeanFactory().registerBean(bean); + } catch (NoSuchBeanDefinitionException e) { + logger.info("Please use a version of micrometer higher than 1.10.0 :{}" + e.getMessage()); + } } @Configuration(proxyBeanMethods = false) @@ -90,41 +98,44 @@ public class DubboObservationAutoConfiguration implements BeanFactoryAware, Smar static class OnlyMetricsConfiguration { @Bean + @ConditionalOnClass(name = "io.micrometer.core.instrument.observation.MeterObservationHandler") ObservationHandlerGrouping metricsObservationHandlerGrouping() { - return new ObservationHandlerGrouping(MeterObservationHandler.class); + return new ObservationHandlerGrouping(io.micrometer.core.instrument.observation.MeterObservationHandler.class); } } @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(Tracer.class) + @ConditionalOnClass(io.micrometer.tracing.Tracer.class) @ConditionalOnMissingClass("io.micrometer.core.instrument.MeterRegistry") @ConditionalOnMissingBean(type = "org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryPostProcessor") static class OnlyTracingConfiguration { @Bean + @ConditionalOnClass(name = "io.micrometer.tracing.handler.TracingObservationHandler") ObservationHandlerGrouping tracingObservationHandlerGrouping() { - return new ObservationHandlerGrouping(TracingObservationHandler.class); + return new ObservationHandlerGrouping(io.micrometer.tracing.handler.TracingObservationHandler.class); } } @Configuration(proxyBeanMethods = false) - @ConditionalOnClass({MeterRegistry.class, Tracer.class}) + @ConditionalOnClass({MeterRegistry.class, io.micrometer.tracing.Tracer.class}) @ConditionalOnMissingBean(type = "org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryPostProcessor") static class MetricsWithTracingConfiguration { @Bean + @ConditionalOnClass(name = {"io.micrometer.tracing.handler.TracingObservationHandler", "io.micrometer.core.instrument.observation.MeterObservationHandler"}) ObservationHandlerGrouping metricsAndTracingObservationHandlerGrouping() { return new ObservationHandlerGrouping( - Arrays.asList(TracingObservationHandler.class, MeterObservationHandler.class)); + Arrays.asList(io.micrometer.tracing.handler.TracingObservationHandler.class, io.micrometer.core.instrument.observation.MeterObservationHandler.class)); } } @Configuration(proxyBeanMethods = false) @ConditionalOnBean(MeterRegistry.class) - @ConditionalOnMissingBean(MeterObservationHandler.class) + @ConditionalOnMissingBean(io.micrometer.core.instrument.observation.MeterObservationHandler.class) static class MeterObservationHandlerConfiguration { @ConditionalOnMissingBean(type = "io.micrometer.tracing.Tracer") @@ -132,21 +143,23 @@ public class DubboObservationAutoConfiguration implements BeanFactoryAware, Smar static class OnlyMetricsMeterObservationHandlerConfiguration { @Bean - DefaultMeterObservationHandler defaultMeterObservationHandler(MeterRegistry meterRegistry) { - return new DefaultMeterObservationHandler(meterRegistry); + @ConditionalOnClass(name = {"io.micrometer.core.instrument.observation.DefaultMeterObservationHandler"}) + io.micrometer.core.instrument.observation.DefaultMeterObservationHandler defaultMeterObservationHandler(MeterRegistry meterRegistry) { + return new io.micrometer.core.instrument.observation.DefaultMeterObservationHandler(meterRegistry); } } - @ConditionalOnBean(Tracer.class) + @ConditionalOnBean(io.micrometer.tracing.Tracer.class) @Configuration(proxyBeanMethods = false) static class TracingAndMetricsObservationHandlerConfiguration { @Bean - TracingAwareMeterObservationHandler tracingAwareMeterObservationHandler( - MeterRegistry meterRegistry, Tracer tracer) { - DefaultMeterObservationHandler delegate = new DefaultMeterObservationHandler(meterRegistry); - return new TracingAwareMeterObservationHandler<>(delegate, tracer); + @ConditionalOnClass(name = {"io.micrometer.tracing.handler.TracingAwareMeterObservationHandler", "io.micrometer.tracing.Tracer"}) + io.micrometer.tracing.handler.TracingAwareMeterObservationHandler tracingAwareMeterObservationHandler( + MeterRegistry meterRegistry, io.micrometer.tracing.Tracer tracer) { + io.micrometer.core.instrument.observation.DefaultMeterObservationHandler delegate = new io.micrometer.core.instrument.observation.DefaultMeterObservationHandler(meterRegistry); + return new io.micrometer.tracing.handler.TracingAwareMeterObservationHandler<>(delegate, tracer); } } diff --git a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/ObservabilityUtils.java b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/ObservabilityUtils.java new file mode 100644 index 0000000000..56ab8ec442 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/ObservabilityUtils.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.observability.autoconfigure; + +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_PREFIX; +import static org.apache.dubbo.spring.boot.util.DubboUtils.PROPERTY_NAME_SEPARATOR; + +/** + * The utilities class for Dubbo Observability + * + * @since 3.2.0 + */ +public class ObservabilityUtils { + + public static final String DUBBO_TRACING_PREFIX = DUBBO_PREFIX + PROPERTY_NAME_SEPARATOR + "tracing" + PROPERTY_NAME_SEPARATOR; + + public static final String DUBBO_TRACING_PROPAGATION = DUBBO_TRACING_PREFIX + "propagation"; + + public static final String DUBBO_TRACING_BAGGAGE = DUBBO_TRACING_PREFIX + "baggage"; + + public static final String DUBBO_TRACING_BAGGAGE_CORRELATION = DUBBO_TRACING_BAGGAGE + PROPERTY_NAME_SEPARATOR + "correlation"; + + public static final String DUBBO_TRACING_BAGGAGE_ENABLED = DUBBO_TRACING_BAGGAGE + PROPERTY_NAME_SEPARATOR + "enabled"; + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/brave/BraveAutoConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/brave/BraveAutoConfiguration.java index dd6c1cc6cc..b3c0bc7f8f 100644 --- a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/brave/BraveAutoConfiguration.java +++ b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/brave/BraveAutoConfiguration.java @@ -16,39 +16,12 @@ */ package org.apache.dubbo.spring.boot.observability.autoconfigure.brave; -import brave.CurrentSpanCustomizer; -import brave.SpanCustomizer; -import brave.Tracing; -import brave.TracingCustomizer; -import brave.baggage.BaggageField; -import brave.baggage.BaggagePropagation; -import brave.baggage.BaggagePropagationConfig; -import brave.baggage.BaggagePropagationCustomizer; -import brave.baggage.CorrelationScopeConfig; -import brave.baggage.CorrelationScopeCustomizer; -import brave.baggage.CorrelationScopeDecorator; -import brave.context.slf4j.MDCScopeDecorator; -import brave.handler.SpanHandler; -import brave.propagation.B3Propagation; -import brave.propagation.CurrentTraceContext; -import brave.propagation.CurrentTraceContextCustomizer; -import brave.propagation.Propagation; -import brave.propagation.ThreadLocalCurrentTraceContext; -import brave.sampler.Sampler; -import io.micrometer.tracing.Tracer; -import io.micrometer.tracing.brave.bridge.BraveBaggageManager; -import io.micrometer.tracing.brave.bridge.BraveCurrentTraceContext; -import io.micrometer.tracing.brave.bridge.BravePropagator; -import io.micrometer.tracing.brave.bridge.BraveSpanCustomizer; -import io.micrometer.tracing.brave.bridge.BraveTracer; -import io.micrometer.tracing.brave.bridge.CompositeSpanHandler; -import io.micrometer.tracing.brave.bridge.W3CPropagation; -import io.micrometer.tracing.exporter.SpanExportingPredicate; -import io.micrometer.tracing.exporter.SpanFilter; -import io.micrometer.tracing.exporter.SpanReporter; + + +import org.apache.dubbo.spring.boot.autoconfigure.DubboConfigurationProperties; import org.apache.dubbo.spring.boot.observability.annotation.ConditionalOnDubboTracingEnable; import org.apache.dubbo.spring.boot.observability.autoconfigure.DubboMicrometerTracingAutoConfiguration; -import org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties; +import org.apache.dubbo.spring.boot.observability.autoconfigure.ObservabilityUtils; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -65,16 +38,17 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; + /** * provider Brave when you are using Boot <3.0 or you are not using spring-boot-starter-actuator */ @AutoConfiguration(before = DubboMicrometerTracingAutoConfiguration.class, afterName = "org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfiguration") -@ConditionalOnClass({Tracer.class, BraveTracer.class}) -@EnableConfigurationProperties(DubboTracingProperties.class) +@ConditionalOnClass(name={"io.micrometer.tracing.Tracer", "io.micrometer.tracing.brave.bridge.BraveTracer","io.micrometer.tracing.brave.bridge.BraveBaggageManager","brave.Tracing"}) +@EnableConfigurationProperties(DubboConfigurationProperties.class) @ConditionalOnDubboTracingEnable public class BraveAutoConfiguration { - private static final BraveBaggageManager BRAVE_BAGGAGE_MANAGER = new BraveBaggageManager(); + private static final io.micrometer.tracing.brave.bridge.BraveBaggageManager BRAVE_BAGGAGE_MANAGER = new io.micrometer.tracing.brave.bridge.BraveBaggageManager(); /** * Default value for application name if {@code spring.application.name} is not set. @@ -84,24 +58,24 @@ public class BraveAutoConfiguration { @Bean @ConditionalOnMissingBean @Order(Ordered.HIGHEST_PRECEDENCE) - CompositeSpanHandler compositeSpanHandler(ObjectProvider predicates, - ObjectProvider reporters, ObjectProvider filters) { - return new CompositeSpanHandler(predicates.orderedStream().collect(Collectors.toList()), + io.micrometer.tracing.brave.bridge.CompositeSpanHandler compositeSpanHandler(ObjectProvider predicates, + ObjectProvider reporters, ObjectProvider filters) { + return new io.micrometer.tracing.brave.bridge.CompositeSpanHandler(predicates.orderedStream().collect(Collectors.toList()), reporters.orderedStream().collect(Collectors.toList()), filters.orderedStream().collect(Collectors.toList())); } @Bean @ConditionalOnMissingBean - public Tracing braveTracing(Environment environment, List spanHandlers, - List tracingCustomizers, CurrentTraceContext currentTraceContext, - Propagation.Factory propagationFactory, Sampler sampler) { + public brave.Tracing braveTracing(Environment environment, List spanHandlers, + List tracingCustomizers, brave.propagation.CurrentTraceContext currentTraceContext, + brave.propagation.Propagation.Factory propagationFactory, brave.sampler.Sampler sampler) { String applicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME); - Tracing.Builder builder = Tracing.newBuilder().currentTraceContext(currentTraceContext).traceId128Bit(true) + brave.Tracing.Builder builder = brave.Tracing.newBuilder().currentTraceContext(currentTraceContext).traceId128Bit(true) .supportsJoin(false).propagationFactory(propagationFactory).sampler(sampler) .localServiceName(applicationName); spanHandlers.forEach(builder::addSpanHandler); - for (TracingCustomizer tracingCustomizer : tracingCustomizers) { + for (brave.TracingCustomizer tracingCustomizer : tracingCustomizers) { tracingCustomizer.customize(builder); } return builder.build(); @@ -109,17 +83,17 @@ public class BraveAutoConfiguration { @Bean @ConditionalOnMissingBean - public brave.Tracer braveTracer(Tracing tracing) { + public brave.Tracer braveTracer(brave.Tracing tracing) { return tracing.tracer(); } @Bean @ConditionalOnMissingBean - public CurrentTraceContext braveCurrentTraceContext(List scopeDecorators, - List currentTraceContextCustomizers) { - ThreadLocalCurrentTraceContext.Builder builder = ThreadLocalCurrentTraceContext.newBuilder(); + public brave.propagation.CurrentTraceContext braveCurrentTraceContext(List scopeDecorators, + List currentTraceContextCustomizers) { + brave.propagation.ThreadLocalCurrentTraceContext.Builder builder = brave.propagation.ThreadLocalCurrentTraceContext.newBuilder(); scopeDecorators.forEach(builder::addScopeDecorator); - for (CurrentTraceContextCustomizer currentTraceContextCustomizer : currentTraceContextCustomizers) { + for (brave.propagation.CurrentTraceContextCustomizer currentTraceContextCustomizer : currentTraceContextCustomizers) { currentTraceContextCustomizer.customize(builder); } return builder.build(); @@ -127,47 +101,47 @@ public class BraveAutoConfiguration { @Bean @ConditionalOnMissingBean - public Sampler braveSampler(DubboTracingProperties properties) { - return Sampler.create(properties.getSampling().getProbability()); + public brave.sampler.Sampler braveSampler(DubboConfigurationProperties properties) { + return brave.sampler.Sampler.create(properties.getTracing().getSampling().getProbability()); } @Bean @ConditionalOnMissingBean(io.micrometer.tracing.Tracer.class) - BraveTracer braveTracerBridge(brave.Tracer tracer, CurrentTraceContext currentTraceContext) { - return new BraveTracer(tracer, new BraveCurrentTraceContext(currentTraceContext), BRAVE_BAGGAGE_MANAGER); + io.micrometer.tracing.brave.bridge.BraveTracer braveTracerBridge(brave.Tracer tracer, brave.propagation.CurrentTraceContext currentTraceContext) { + return new io.micrometer.tracing.brave.bridge.BraveTracer(tracer, new io.micrometer.tracing.brave.bridge.BraveCurrentTraceContext(currentTraceContext), BRAVE_BAGGAGE_MANAGER); } @Bean @ConditionalOnMissingBean - BravePropagator bravePropagator(Tracing tracing) { - return new BravePropagator(tracing); + io.micrometer.tracing.brave.bridge.BravePropagator bravePropagator(brave.Tracing tracing) { + return new io.micrometer.tracing.brave.bridge.BravePropagator(tracing); } @Bean - @ConditionalOnMissingBean(SpanCustomizer.class) - CurrentSpanCustomizer currentSpanCustomizer(Tracing tracing) { - return CurrentSpanCustomizer.create(tracing); + @ConditionalOnMissingBean(brave.SpanCustomizer.class) + brave.CurrentSpanCustomizer currentSpanCustomizer(brave.Tracing tracing) { + return brave.CurrentSpanCustomizer.create(tracing); } @Bean @ConditionalOnMissingBean(io.micrometer.tracing.SpanCustomizer.class) - BraveSpanCustomizer braveSpanCustomizer(SpanCustomizer spanCustomizer) { - return new BraveSpanCustomizer(spanCustomizer); + io.micrometer.tracing.brave.bridge.BraveSpanCustomizer braveSpanCustomizer(brave.SpanCustomizer spanCustomizer) { + return new io.micrometer.tracing.brave.bridge.BraveSpanCustomizer(spanCustomizer); } @Configuration(proxyBeanMethods = false) - @ConditionalOnProperty(value = "dubbo.tracing.baggage.enabled", havingValue = "false") + @ConditionalOnProperty(value = ObservabilityUtils.DUBBO_TRACING_BAGGAGE_ENABLED, havingValue = "false") static class BraveNoBaggageConfiguration { @Bean @ConditionalOnMissingBean - Propagation.Factory propagationFactory(DubboTracingProperties tracing) { - DubboTracingProperties.Propagation.PropagationType type = tracing.getPropagation().getType(); + brave.propagation.Propagation.Factory propagationFactory(DubboConfigurationProperties tracing) { + String type = tracing.getTracing().getPropagation().getType(); switch (type) { - case B3: - return B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build(); - case W3C: - return new W3CPropagation(); + case org.apache.dubbo.config.nested.PropagationConfig.B3: + return brave.propagation.B3Propagation.newFactoryBuilder().injectFormat(brave.propagation.B3Propagation.Format.SINGLE_NO_PARENT).build(); + case org.apache.dubbo.config.nested.PropagationConfig.W3C: + return new io.micrometer.tracing.brave.bridge.W3CPropagation(); default: throw new IllegalArgumentException("UnSupport propagation type"); } @@ -175,36 +149,36 @@ public class BraveAutoConfiguration { } - @ConditionalOnProperty(value = "dubbo.tracing.baggage.enabled", matchIfMissing = true) + @ConditionalOnProperty(value = ObservabilityUtils.DUBBO_TRACING_BAGGAGE_ENABLED, matchIfMissing = true) @Configuration(proxyBeanMethods = false) static class BraveBaggageConfiguration { - private final DubboTracingProperties dubboTracingProperties; + private final DubboConfigurationProperties dubboConfigProperties; - public BraveBaggageConfiguration(DubboTracingProperties dubboTracingProperties) { - this.dubboTracingProperties = dubboTracingProperties; + public BraveBaggageConfiguration(DubboConfigurationProperties dubboConfigProperties) { + this.dubboConfigProperties = dubboConfigProperties; } @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(prefix = "dubbo.tracing.propagation", value = "type", havingValue = "B3") - BaggagePropagation.FactoryBuilder b3PropagationFactoryBuilder( - ObjectProvider baggagePropagationCustomizers) { - Propagation.Factory delegate = - B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build(); + @ConditionalOnProperty(prefix = ObservabilityUtils.DUBBO_TRACING_PROPAGATION, value = "type", havingValue = "B3") + brave.baggage.BaggagePropagation.FactoryBuilder b3PropagationFactoryBuilder( + ObjectProvider baggagePropagationCustomizers) { + brave.propagation.Propagation.Factory delegate = + brave.propagation.B3Propagation.newFactoryBuilder().injectFormat(brave.propagation.B3Propagation.Format.SINGLE_NO_PARENT).build(); - BaggagePropagation.FactoryBuilder builder = BaggagePropagation.newFactoryBuilder(delegate); + brave.baggage.BaggagePropagation.FactoryBuilder builder = brave.baggage.BaggagePropagation.newFactoryBuilder(delegate); baggagePropagationCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder; } @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(prefix = "dubbo.tracing.propagation", value = "type", havingValue = "W3C", matchIfMissing = true) - BaggagePropagation.FactoryBuilder w3cPropagationFactoryBuilder( - ObjectProvider baggagePropagationCustomizers) { - Propagation.Factory delegate = new W3CPropagation(BRAVE_BAGGAGE_MANAGER, Collections.emptyList()); + @ConditionalOnProperty(prefix = ObservabilityUtils.DUBBO_TRACING_PROPAGATION, value = "type", havingValue = "W3C", matchIfMissing = true) + brave.baggage.BaggagePropagation.FactoryBuilder w3cPropagationFactoryBuilder( + ObjectProvider baggagePropagationCustomizers) { + brave.propagation.Propagation.Factory delegate = new io.micrometer.tracing.brave.bridge.W3CPropagation(BRAVE_BAGGAGE_MANAGER, Collections.emptyList()); - BaggagePropagation.FactoryBuilder builder = BaggagePropagation.newFactoryBuilder(delegate); + brave.baggage.BaggagePropagation.FactoryBuilder builder = brave.baggage.BaggagePropagation.newFactoryBuilder(delegate); baggagePropagationCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder; } @@ -212,47 +186,47 @@ public class BraveAutoConfiguration { @Bean @ConditionalOnMissingBean @Order(0) - BaggagePropagationCustomizer remoteFieldsBaggagePropagationCustomizer() { + brave.baggage.BaggagePropagationCustomizer remoteFieldsBaggagePropagationCustomizer() { return (builder) -> { - List remoteFields = dubboTracingProperties.getBaggage().getRemoteFields(); + List remoteFields = dubboConfigProperties.getTracing().getBaggage().getRemoteFields(); for (String fieldName : remoteFields) { - builder.add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create(fieldName))); + builder.add(brave.baggage.BaggagePropagationConfig.SingleBaggageField.remote(brave.baggage.BaggageField.create(fieldName))); } }; } @Bean @ConditionalOnMissingBean - Propagation.Factory propagationFactory(BaggagePropagation.FactoryBuilder factoryBuilder) { + brave.propagation.Propagation.Factory propagationFactory(brave.baggage.BaggagePropagation.FactoryBuilder factoryBuilder) { return factoryBuilder.build(); } @Bean @ConditionalOnMissingBean - CorrelationScopeDecorator.Builder mdcCorrelationScopeDecoratorBuilder( - ObjectProvider correlationScopeCustomizers) { - CorrelationScopeDecorator.Builder builder = MDCScopeDecorator.newBuilder(); + brave.baggage.CorrelationScopeDecorator.Builder mdcCorrelationScopeDecoratorBuilder( + ObjectProvider correlationScopeCustomizers) { + brave.baggage.CorrelationScopeDecorator.Builder builder = brave.context.slf4j.MDCScopeDecorator.newBuilder(); correlationScopeCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder; } @Bean @Order(0) - @ConditionalOnProperty(prefix = "dubbo.tracing.baggage.correlation", name = "enabled", - matchIfMissing = true) - CorrelationScopeCustomizer correlationFieldsCorrelationScopeCustomizer() { + @ConditionalOnProperty(prefix = ObservabilityUtils.DUBBO_TRACING_BAGGAGE_CORRELATION, name = "enabled", + matchIfMissing = true) + brave.baggage.CorrelationScopeCustomizer correlationFieldsCorrelationScopeCustomizer() { return (builder) -> { - List correlationFields = this.dubboTracingProperties.getBaggage().getCorrelation().getFields(); + List correlationFields = this.dubboConfigProperties.getTracing().getBaggage().getCorrelation().getFields(); for (String field : correlationFields) { - builder.add(CorrelationScopeConfig.SingleCorrelationField.newBuilder(BaggageField.create(field)) - .flushOnUpdate().build()); + builder.add(brave.baggage.CorrelationScopeConfig.SingleCorrelationField.newBuilder(brave.baggage.BaggageField.create(field)) + .flushOnUpdate().build()); } }; } @Bean - @ConditionalOnMissingBean(CurrentTraceContext.ScopeDecorator.class) - CurrentTraceContext.ScopeDecorator correlationScopeDecorator(CorrelationScopeDecorator.Builder builder) { + @ConditionalOnMissingBean(brave.propagation.CurrentTraceContext.ScopeDecorator.class) + brave.propagation.CurrentTraceContext.ScopeDecorator correlationScopeDecorator(brave.baggage.CorrelationScopeDecorator.Builder builder) { return builder.build(); } diff --git a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/otel/OpenTelemetryAutoConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/otel/OpenTelemetryAutoConfiguration.java index c2903fa46d..b17e2cdb47 100644 --- a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/otel/OpenTelemetryAutoConfiguration.java +++ b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/autoconfigure/otel/OpenTelemetryAutoConfiguration.java @@ -16,43 +16,12 @@ */ package org.apache.dubbo.spring.boot.observability.autoconfigure.otel; -import io.micrometer.tracing.SpanCustomizer; -import io.micrometer.tracing.exporter.SpanExportingPredicate; -import io.micrometer.tracing.exporter.SpanFilter; -import io.micrometer.tracing.exporter.SpanReporter; -import io.micrometer.tracing.otel.bridge.CompositeSpanExporter; -import io.micrometer.tracing.otel.bridge.EventListener; -import io.micrometer.tracing.otel.bridge.EventPublishingContextWrapper; -import io.micrometer.tracing.otel.bridge.OtelBaggageManager; -import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext; -import io.micrometer.tracing.otel.bridge.OtelPropagator; -import io.micrometer.tracing.otel.bridge.OtelSpanCustomizer; -import io.micrometer.tracing.otel.bridge.OtelTracer; -import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener; -import io.micrometer.tracing.otel.bridge.Slf4JEventListener; -import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; -import io.opentelemetry.context.ContextStorage; -import io.opentelemetry.context.propagation.ContextPropagators; -import io.opentelemetry.context.propagation.TextMapPropagator; -import io.opentelemetry.extension.trace.propagation.B3Propagator; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.trace.SdkTracerProvider; -import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; -import io.opentelemetry.sdk.trace.SpanProcessor; -import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import io.opentelemetry.sdk.trace.samplers.Sampler; -import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; + import org.apache.dubbo.common.Version; +import org.apache.dubbo.spring.boot.autoconfigure.DubboConfigurationProperties; import org.apache.dubbo.spring.boot.observability.annotation.ConditionalOnDubboTracingEnable; import org.apache.dubbo.spring.boot.observability.autoconfigure.DubboMicrometerTracingAutoConfiguration; -import org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties; +import org.apache.dubbo.spring.boot.observability.autoconfigure.ObservabilityUtils; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -72,8 +41,10 @@ import java.util.stream.Collectors; */ @AutoConfiguration(before = DubboMicrometerTracingAutoConfiguration.class, afterName = "org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration") @ConditionalOnDubboTracingEnable -@ConditionalOnClass({OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class}) -@EnableConfigurationProperties(DubboTracingProperties.class) +@ConditionalOnClass(name = {"io.micrometer.tracing.otel.bridge.OtelTracer", + "io.opentelemetry.sdk.trace.SdkTracerProvider", "io.opentelemetry.api.OpenTelemetry" + , "io.micrometer.tracing.SpanCustomizer"}) +@EnableConfigurationProperties(DubboConfigurationProperties.class) public class OpenTelemetryAutoConfiguration { /** @@ -81,172 +52,172 @@ public class OpenTelemetryAutoConfiguration { */ private static final String DEFAULT_APPLICATION_NAME = "application"; - private final DubboTracingProperties dubboTracingProperties; + private final DubboConfigurationProperties dubboConfigProperties; - OpenTelemetryAutoConfiguration(DubboTracingProperties dubboTracingProperties) { - this.dubboTracingProperties = dubboTracingProperties; + OpenTelemetryAutoConfiguration(DubboConfigurationProperties dubboConfigProperties) { + this.dubboConfigProperties = dubboConfigProperties; } @Bean @ConditionalOnMissingBean - OpenTelemetry openTelemetry(SdkTracerProvider sdkTracerProvider, ContextPropagators contextPropagators) { - return OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider).setPropagators(contextPropagators) + io.opentelemetry.api.OpenTelemetry openTelemetry(io.opentelemetry.sdk.trace.SdkTracerProvider sdkTracerProvider, io.opentelemetry.context.propagation.ContextPropagators contextPropagators) { + return io.opentelemetry.sdk.OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider).setPropagators(contextPropagators) .build(); } @Bean @ConditionalOnMissingBean - SdkTracerProvider otelSdkTracerProvider(Environment environment, ObjectProvider spanProcessors, - Sampler sampler) { + io.opentelemetry.sdk.trace.SdkTracerProvider otelSdkTracerProvider(Environment environment, ObjectProvider spanProcessors, + io.opentelemetry.sdk.trace.samplers.Sampler sampler) { String applicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME); - SdkTracerProviderBuilder builder = SdkTracerProvider.builder().setSampler(sampler) - .setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName))); + io.opentelemetry.sdk.trace.SdkTracerProviderBuilder builder = io.opentelemetry.sdk.trace.SdkTracerProvider.builder().setSampler(sampler) + .setResource(io.opentelemetry.sdk.resources.Resource.create(io.opentelemetry.api.common.Attributes.of(io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME, applicationName))); spanProcessors.orderedStream().forEach(builder::addSpanProcessor); return builder.build(); } @Bean @ConditionalOnMissingBean - ContextPropagators otelContextPropagators(ObjectProvider textMapPropagators) { - return ContextPropagators.create(TextMapPropagator.composite(textMapPropagators.orderedStream().collect(Collectors.toList()))); + io.opentelemetry.context.propagation.ContextPropagators otelContextPropagators(ObjectProvider textMapPropagators) { + return io.opentelemetry.context.propagation.ContextPropagators.create(io.opentelemetry.context.propagation.TextMapPropagator.composite(textMapPropagators.orderedStream().collect(Collectors.toList()))); } @Bean @ConditionalOnMissingBean - Sampler otelSampler() { - Sampler rootSampler = Sampler.traceIdRatioBased(this.dubboTracingProperties.getSampling().getProbability()); - return Sampler.parentBased(rootSampler); + io.opentelemetry.sdk.trace.samplers.Sampler otelSampler() { + io.opentelemetry.sdk.trace.samplers.Sampler rootSampler = io.opentelemetry.sdk.trace.samplers.Sampler.traceIdRatioBased(this.dubboConfigProperties.getTracing().getSampling().getProbability()); + return io.opentelemetry.sdk.trace.samplers.Sampler.parentBased(rootSampler); } @Bean @ConditionalOnMissingBean - SpanProcessor otelSpanProcessor(ObjectProvider spanExporters, - ObjectProvider spanExportingPredicates, ObjectProvider spanReporters, - ObjectProvider spanFilters) { - return BatchSpanProcessor.builder(new CompositeSpanExporter(spanExporters.orderedStream().collect(Collectors.toList()), + io.opentelemetry.sdk.trace.SpanProcessor otelSpanProcessor(ObjectProvider spanExporters, + ObjectProvider spanExportingPredicates, ObjectProvider spanReporters, + ObjectProvider spanFilters) { + return io.opentelemetry.sdk.trace.export.BatchSpanProcessor.builder(new io.micrometer.tracing.otel.bridge.CompositeSpanExporter(spanExporters.orderedStream().collect(Collectors.toList()), spanExportingPredicates.orderedStream().collect(Collectors.toList()), spanReporters.orderedStream().collect(Collectors.toList()), spanFilters.orderedStream().collect(Collectors.toList()))).build(); } @Bean @ConditionalOnMissingBean - Tracer otelTracer(OpenTelemetry openTelemetry) { + io.opentelemetry.api.trace.Tracer otelTracer(io.opentelemetry.api.OpenTelemetry openTelemetry) { return openTelemetry.getTracer("org.apache.dubbo", Version.getVersion()); } @Bean @ConditionalOnMissingBean(io.micrometer.tracing.Tracer.class) - OtelTracer micrometerOtelTracer(Tracer tracer, OtelTracer.EventPublisher eventPublisher, - OtelCurrentTraceContext otelCurrentTraceContext) { - return new OtelTracer(tracer, otelCurrentTraceContext, eventPublisher, - new OtelBaggageManager(otelCurrentTraceContext, this.dubboTracingProperties.getBaggage().getRemoteFields(), - Collections.emptyList())); + io.micrometer.tracing.otel.bridge.OtelTracer micrometerOtelTracer(io.opentelemetry.api.trace.Tracer tracer, io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher eventPublisher, + io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext otelCurrentTraceContext) { + return new io.micrometer.tracing.otel.bridge.OtelTracer(tracer, otelCurrentTraceContext, eventPublisher, + new io.micrometer.tracing.otel.bridge.OtelBaggageManager(otelCurrentTraceContext, this.dubboConfigProperties.getTracing().getBaggage().getRemoteFields(), + Collections.emptyList())); } @Bean @ConditionalOnMissingBean - OtelPropagator otelPropagator(ContextPropagators contextPropagators, Tracer tracer) { - return new OtelPropagator(contextPropagators, tracer); + io.micrometer.tracing.otel.bridge.OtelPropagator otelPropagator(io.opentelemetry.context.propagation.ContextPropagators contextPropagators, io.opentelemetry.api.trace.Tracer tracer) { + return new io.micrometer.tracing.otel.bridge.OtelPropagator(contextPropagators, tracer); } @Bean @ConditionalOnMissingBean - OtelTracer.EventPublisher otelTracerEventPublisher(List eventListeners) { + io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher otelTracerEventPublisher(List eventListeners) { return new OTelEventPublisher(eventListeners); } @Bean @ConditionalOnMissingBean - OtelCurrentTraceContext otelCurrentTraceContext(OtelTracer.EventPublisher publisher) { - ContextStorage.addWrapper(new EventPublishingContextWrapper(publisher)); - return new OtelCurrentTraceContext(); + io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext otelCurrentTraceContext(io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher publisher) { + io.opentelemetry.context.ContextStorage.addWrapper(new io.micrometer.tracing.otel.bridge.EventPublishingContextWrapper(publisher)); + return new io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext(); } @Bean @ConditionalOnMissingBean - Slf4JEventListener otelSlf4JEventListener() { - return new Slf4JEventListener(); + io.micrometer.tracing.otel.bridge.Slf4JEventListener otelSlf4JEventListener() { + return new io.micrometer.tracing.otel.bridge.Slf4JEventListener(); } @Bean - @ConditionalOnMissingBean(SpanCustomizer.class) - OtelSpanCustomizer otelSpanCustomizer() { - return new OtelSpanCustomizer(); + @ConditionalOnMissingBean(io.micrometer.tracing.SpanCustomizer.class) + io.micrometer.tracing.otel.bridge.OtelSpanCustomizer otelSpanCustomizer() { + return new io.micrometer.tracing.otel.bridge.OtelSpanCustomizer(); } @Configuration(proxyBeanMethods = false) - @ConditionalOnProperty(prefix = "dubbo.tracing.baggage", name = "enabled", matchIfMissing = true) + @ConditionalOnProperty(prefix = ObservabilityUtils.DUBBO_TRACING_BAGGAGE, name = "enabled", matchIfMissing = true) static class BaggageConfiguration { - private final DubboTracingProperties tracingProperties; + private final DubboConfigurationProperties dubboConfigProperties; - BaggageConfiguration(DubboTracingProperties tracingProperties) { - this.tracingProperties = tracingProperties; + BaggageConfiguration(DubboConfigurationProperties dubboConfigProperties) { + this.dubboConfigProperties = dubboConfigProperties; } @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(prefix = "dubbo.tracing.propagation", name = "type", havingValue = "W3C", - matchIfMissing = true) - TextMapPropagator w3cTextMapPropagatorWithBaggage(OtelCurrentTraceContext otelCurrentTraceContext) { - List remoteFields = this.tracingProperties.getBaggage().getRemoteFields(); - return TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), - W3CBaggagePropagator.getInstance(), new BaggageTextMapPropagator(remoteFields, - new OtelBaggageManager(otelCurrentTraceContext, remoteFields, Collections.emptyList()))); + @ConditionalOnProperty(prefix = ObservabilityUtils.DUBBO_TRACING_PROPAGATION, name = "type", havingValue = "W3C", + matchIfMissing = true) + io.opentelemetry.context.propagation.TextMapPropagator w3cTextMapPropagatorWithBaggage(io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext otelCurrentTraceContext) { + List remoteFields = this.dubboConfigProperties.getTracing().getBaggage().getRemoteFields(); + return io.opentelemetry.context.propagation.TextMapPropagator.composite(io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator.getInstance(), + io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator.getInstance(), new io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator(remoteFields, + new io.micrometer.tracing.otel.bridge.OtelBaggageManager(otelCurrentTraceContext, remoteFields, Collections.emptyList()))); } @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(prefix = "dubbo.tracing.propagation", name = "type", havingValue = "B3") - TextMapPropagator b3BaggageTextMapPropagator(OtelCurrentTraceContext otelCurrentTraceContext) { - List remoteFields = this.tracingProperties.getBaggage().getRemoteFields(); - return TextMapPropagator.composite(B3Propagator.injectingSingleHeader(), - new BaggageTextMapPropagator(remoteFields, - new OtelBaggageManager(otelCurrentTraceContext, remoteFields, Collections.emptyList()))); + @ConditionalOnProperty(prefix = ObservabilityUtils.DUBBO_TRACING_PROPAGATION, name = "type", havingValue = "B3") + io.opentelemetry.context.propagation.TextMapPropagator b3BaggageTextMapPropagator(io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext otelCurrentTraceContext) { + List remoteFields = this.dubboConfigProperties.getTracing().getBaggage().getRemoteFields(); + return io.opentelemetry.context.propagation.TextMapPropagator.composite(io.opentelemetry.extension.trace.propagation.B3Propagator.injectingSingleHeader(), + new io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator(remoteFields, + new io.micrometer.tracing.otel.bridge.OtelBaggageManager(otelCurrentTraceContext, remoteFields, Collections.emptyList()))); } @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(prefix = "dubbo.tracing.baggage.correlation", name = "enabled", - matchIfMissing = true) - Slf4JBaggageEventListener otelSlf4JBaggageEventListener() { - return new Slf4JBaggageEventListener(this.tracingProperties.getBaggage().getCorrelation().getFields()); + @ConditionalOnProperty(prefix = ObservabilityUtils.DUBBO_TRACING_BAGGAGE_CORRELATION, name = "enabled", + matchIfMissing = true) + io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener otelSlf4JBaggageEventListener() { + return new io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener(this.dubboConfigProperties.getTracing().getBaggage().getCorrelation().getFields()); } } @Configuration(proxyBeanMethods = false) - @ConditionalOnProperty(prefix = "dubbo.tracing.baggage", name = "enabled", havingValue = "false") + @ConditionalOnProperty(prefix = ObservabilityUtils.DUBBO_TRACING_BAGGAGE, name = "enabled", havingValue = "false") static class NoBaggageConfiguration { @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(prefix = "dubbo.tracing.propagation", name = "type", havingValue = "B3") - B3Propagator b3TextMapPropagator() { - return B3Propagator.injectingSingleHeader(); + @ConditionalOnProperty(prefix = ObservabilityUtils.DUBBO_TRACING_PROPAGATION, name = "type", havingValue = "B3") + io.opentelemetry.extension.trace.propagation.B3Propagator b3TextMapPropagator() { + return io.opentelemetry.extension.trace.propagation.B3Propagator.injectingSingleHeader(); } @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(prefix = "dubbo.tracing.propagation", name = "type", havingValue = "W3C", - matchIfMissing = true) - W3CTraceContextPropagator w3cTextMapPropagatorWithoutBaggage() { - return W3CTraceContextPropagator.getInstance(); + @ConditionalOnProperty(prefix = ObservabilityUtils.DUBBO_TRACING_PROPAGATION, name = "type", havingValue = "W3C", + matchIfMissing = true) + io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator w3cTextMapPropagatorWithoutBaggage() { + return io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator.getInstance(); } } - static class OTelEventPublisher implements OtelTracer.EventPublisher { + static class OTelEventPublisher implements io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher { - private final List listeners; + private final List listeners; - OTelEventPublisher(List listeners) { + OTelEventPublisher(List listeners) { this.listeners = listeners; } @Override public void publishEvent(Object event) { - for (EventListener listener : this.listeners) { + for (io.micrometer.tracing.otel.bridge.EventListener listener : this.listeners) { listener.onEvent(event); } } diff --git a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/config/DubboTracingProperties.java b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/config/DubboTracingProperties.java deleted file mode 100644 index 72d4b967df..0000000000 --- a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/java/org/apache/dubbo/spring/boot/observability/config/DubboTracingProperties.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.spring.boot.observability.config; - - -import org.springframework.boot.context.properties.ConfigurationProperties; - -import java.util.ArrayList; -import java.util.List; - -@ConfigurationProperties("dubbo.tracing") -public class DubboTracingProperties { - - /** - * Whether auto-configuration of tracing is enabled. - */ - private boolean enabled = true; - - /** - * Sampling configuration. - */ - private final Sampling sampling = new Sampling(); - - /** - * Baggage configuration. - */ - private final Baggage baggage = new Baggage(); - - /** - * Propagation configuration. - */ - private final Propagation propagation = new Propagation(); - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public Sampling getSampling() { - return this.sampling; - } - - public Baggage getBaggage() { - return this.baggage; - } - - public Propagation getPropagation() { - return this.propagation; - } - - public static class Sampling { - - /** - * Probability in the range from 0.0 to 1.0 that a trace will be sampled. - */ - private float probability = 0.10f; - - public float getProbability() { - return this.probability; - } - - public void setProbability(float probability) { - this.probability = probability; - } - - } - - public static class Baggage { - - /** - * Whether to enable Micrometer Tracing baggage propagation. - */ - private boolean enabled = true; - - /** - * Correlation configuration. - */ - private Correlation correlation = new Correlation(); - - /** - * List of fields that are referenced the same in-process as it is on the wire. - * For example, the field "x-vcap-request-id" would be set as-is including the - * prefix. - */ - private List remoteFields = new ArrayList<>(); - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public Correlation getCorrelation() { - return this.correlation; - } - - public void setCorrelation(Correlation correlation) { - this.correlation = correlation; - } - - public List getRemoteFields() { - return this.remoteFields; - } - - public void setRemoteFields(List remoteFields) { - this.remoteFields = remoteFields; - } - - public static class Correlation { - - /** - * Whether to enable correlation of the baggage context with logging contexts. - */ - private boolean enabled = true; - - /** - * List of fields that should be correlated with the logging context. That - * means that these fields would end up as key-value pairs in e.g. MDC. - */ - private List fields = new ArrayList<>(); - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public List getFields() { - return this.fields; - } - - public void setFields(List fields) { - this.fields = fields; - } - - } - - } - - public static class Propagation { - - /** - * Tracing context propagation type. - */ - private PropagationType type = PropagationType.W3C; - - public PropagationType getType() { - return this.type; - } - - public void setType(PropagationType type) { - this.type = type; - } - - public enum PropagationType { - - /** - * B3 propagation type. - */ - B3, - - /** - * W3C propagation type. - */ - W3C - - } - - } -} diff --git a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/resources/META-INF/spring-configuration-metadata.json b/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/resources/META-INF/spring-configuration-metadata.json deleted file mode 100644 index 3ae084b1f1..0000000000 --- a/dubbo-spring-boot/dubbo-spring-boot-observability-starter/src/main/resources/META-INF/spring-configuration-metadata.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "groups": [ - { - "name": "dubbo.tracing", - "type": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties", - "sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties" - }, - { - "name": "dubbo.tracing.baggage", - "type": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage", - "sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties", - "sourceMethod": "getBaggage()" - }, - { - "name": "dubbo.tracing.baggage.correlation", - "type": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage$Correlation", - "sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage", - "sourceMethod": "getCorrelation()" - }, - { - "name": "dubbo.tracing.propagation", - "type": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Propagation", - "sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties", - "sourceMethod": "getPropagation()" - }, - { - "name": "dubbo.tracing.sampling", - "type": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Sampling", - "sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties", - "sourceMethod": "getSampling()" - } - ], - "properties": [ - { - "name": "dubbo.tracing.baggage.correlation.enabled", - "type": "java.lang.Boolean", - "description": "Whether to enable correlation of the baggage context with logging contexts.", - "sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage$Correlation", - "defaultValue": true - }, - { - "name": "dubbo.tracing.baggage.correlation.fields", - "type": "java.util.List", - "description": "List of fields that should be correlated with the logging context. That means that these fields would end up as key-value pairs in e.g. MDC.", - "sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage$Correlation" - }, - { - "name": "dubbo.tracing.baggage.enabled", - "type": "java.lang.Boolean", - "description": "Whether to enable Micrometer Tracing baggage propagation.", - "sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage", - "defaultValue": true - }, - { - "name": "dubbo.tracing.baggage.remote-fields", - "type": "java.util.List", - "description": "List of fields that are referenced the same in-process as it is on the wire. For example, the field \"x-vcap-request-id\" would be set as-is including the prefix.", - "sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage" - }, - { - "name": "dubbo.tracing.enabled", - "type": "java.lang.Boolean", - "description": "Whether auto-configuration of tracing is enabled.", - "sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties", - "defaultValue": true - }, - { - "name": "dubbo.tracing.propagation.type", - "type": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Propagation$PropagationType", - "description": "Tracing context propagation type.", - "sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Propagation" - }, - { - "name": "dubbo.tracing.sampling.probability", - "type": "java.lang.Float", - "description": "Probability in the range from 0.0 to 1.0 that a trace will be sampled.", - "sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Sampling", - "defaultValue": 0.1 - } - ], - "hints": [] -} diff --git a/dubbo-spring-boot/pom.xml b/dubbo-spring-boot/pom.xml index 8eebf37be6..79997de27c 100644 --- a/dubbo-spring-boot/pom.xml +++ b/dubbo-spring-boot/pom.xml @@ -40,12 +40,12 @@ - 2.7.9 + 2.7.10 ${revision} 2.20.0 - 1.14.2 + 1.14.4 diff --git a/dubbo-test/dubbo-test-check/pom.xml b/dubbo-test/dubbo-test-check/pom.xml index 5b25342305..35cf2ace79 100644 --- a/dubbo-test/dubbo-test-check/pom.xml +++ b/dubbo-test/dubbo-test-check/pom.xml @@ -35,10 +35,10 @@ 4.2.0 3.4.14 4.2.0 - 1.22 - 1.6.2 + 1.23.0 + 1.9.2 1.3 - 2.12.1 + 2.12.3 diff --git a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java index 7f4b3250b4..b66393aca9 100644 --- a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java +++ b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java @@ -17,6 +17,7 @@ package org.apache.dubbo.dependency; import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; @@ -39,6 +40,7 @@ import java.util.stream.Collectors; class FileTest { private final static List ignoredModules = new LinkedList<>(); + private final static List ignoredArtifacts = new LinkedList<>(); private final static List ignoredModulesInDubboAll = new LinkedList<>(); static { @@ -50,6 +52,9 @@ class FileTest { ignoredModules.add(Pattern.compile("dubbo-demo.*")); ignoredModules.add(Pattern.compile("dubbo-annotation-processor")); + ignoredArtifacts.add(Pattern.compile("dubbo-demo.*")); + ignoredArtifacts.add(Pattern.compile("dubbo-test.*")); + ignoredModulesInDubboAll.add(Pattern.compile("dubbo")); ignoredModulesInDubboAll.add(Pattern.compile("dubbo-bom")); ignoredModulesInDubboAll.add(Pattern.compile("dubbo-compiler")); @@ -101,6 +106,40 @@ class FileTest { Assertions.assertTrue(expectedArtifactIds.isEmpty(), "Newly created modules must be added to dubbo-bom. Found modules: " + expectedArtifactIds); } + @Test + void checkArtifacts() throws DocumentException, IOException { + File baseFile = getBaseFile(); + + List poms = new LinkedList<>(); + readPoms(baseFile, poms); + + SAXReader reader = new SAXReader(); + + List artifactIds = poms.stream() + .map(f -> { + try { + return reader.read(f); + } catch (DocumentException e) { + throw new RuntimeException(e); + } + }) + .map(Document::getRootElement) + .map(doc -> doc.elementText("artifactId")) + .sorted() + .collect(Collectors.toList()); + + List artifactIdsInRoot = IOUtils.readLines( + this.getClass().getClassLoader().getResource("META-INF/versions/.artifacts").openStream(), + StandardCharsets.UTF_8); + artifactIdsInRoot.removeIf(s -> s.startsWith("#")); + + List expectedArtifactIds = new LinkedList<>(artifactIds); + expectedArtifactIds.removeAll(artifactIdsInRoot); + expectedArtifactIds.removeIf(artifactId -> ignoredArtifacts.stream().anyMatch(pattern -> pattern.matcher(artifactId).matches())); + + Assertions.assertTrue(expectedArtifactIds.isEmpty(), "Newly created modules must be added to .artifacts (in project root). Found modules: " + expectedArtifactIds); + } + @Test void checkDubboDependenciesAll() throws DocumentException { File baseFile = getBaseFile(); diff --git a/dubbo-xds/pom.xml b/dubbo-xds/pom.xml index d56a6c0897..1263ed7d5c 100644 --- a/dubbo-xds/pom.xml +++ b/dubbo-xds/pom.xml @@ -32,7 +32,7 @@ false 3.22.2 - 1.53.0 + 1.54.0 @@ -104,7 +104,7 @@ kr.motd.maven os-maven-plugin - 1.6.2 + 1.7.1 diff --git a/pom.xml b/pom.xml index a792bd4a95..0239e1827d 100644 --- a/pom.xml +++ b/pom.xml @@ -88,9 +88,9 @@ - 5.8.1 + 5.9.2 4.2.0 - 3.11.1 + 3.12.13 2.2 5.2.4.Final 2.2.5 @@ -122,8 +122,8 @@ 9.4.51.v20230217 3.2.1 0.8.8 - 1.3.0 - 3.2.1 + 1.4.1 + 3.3.0 3.1.0 1.0.0 @@ -307,7 +307,7 @@ com.puppycrawl.tools checkstyle - 8.41 + 8.45.1 org.apache.dubbo @@ -603,26 +603,8 @@ jacoco089 - 0.8.9-SNAPSHOT + 0.8.9 - - - apache.snapshots - Apache Snapshot Repository - https://repository.apache.org/snapshots - - false - - - - oss.snapshots - Oss Repository - https://oss.sonatype.org/content/repositories/snapshots - - false - - - @@ -642,6 +624,14 @@ LICENSE + + ${maven.multiModuleProjectDirectory} + META-INF/versions + false + + .artifacts + + @@ -727,10 +717,40 @@ ${file_encoding} + + org.apache.maven.plugins + maven-antrun-plugin + + + get-version-infos + compile + + true + + + + + + + + + + + + + + + run + + + + org.apache.maven.plugins maven-release-plugin - 3.0.0-M7 + 3.0.0 true false @@ -804,7 +824,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.2.0 + 3.3.0 org.fusesource.hawtjni