diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000000..185873ad02a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,33 @@ +name: Bug Report +description: Create a report to help us improve +title: "[BUG] - Issue Title" +labels: bug + +body: + - type: markdown + attributes: + value: "## Describe the bug\nA clear and concise description of what the bug is." + + - type: textarea + attributes: + label: Steps to Reproduce + description: "List the steps to reproduce the bug." + placeholder: "1. Go to '...'\n2. Click on '...'\n3. See error" + + - type: textarea + attributes: + label: Expected Behavior + description: "Describe the behavior you expected to see." + placeholder: "A clear and concise description of what you expected to happen." + + - type: input + attributes: + label: Servicecomb Version + description: "Please provide your Servicecomb version" + placeholder: "3.2.4" + + - type: textarea + attributes: + label: Additional Context + description: "Add any other context about the problem here." + placeholder: "Any additional information that might help us debug the issue." diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..16fd2512ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,18 @@ +blank_issues_enabled: false +contact_links: + - name: "Contact Support" + url: "mailto:dev@servicecomb.apache.org" + about: "If you need help, please contact us." +templates: + - name: "Bug Report" + description: "Report a bug or issue." + filename: "bug_report.yml" + - name: "Feature Request" + description: "Request a new feature or improvement." + filename: "feature_request.yml" + - name: "Documentation Issue" + description: "Report an issue with documentation." + filename: "documentation_issue.yml" + - name: "Question" + description: "Ask a question or seek help." + filename: "question.yml" diff --git a/.github/ISSUE_TEMPLATE/documentation_issue.yml b/.github/ISSUE_TEMPLATE/documentation_issue.yml new file mode 100644 index 00000000000..94ef3490041 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_issue.yml @@ -0,0 +1,27 @@ +name: Documentation Issue +description: Report an issue with the documentation +title: "[DOCUMENTATION] - Documentation Issue Title" +labels: documentation + +body: + - type: markdown + attributes: + value: "## Describe the documentation issue\nA clear and concise description of the issue with the documentation." + + - type: textarea + attributes: + label: Location in documentation + description: "Please provide the URL or section where the issue appears." + placeholder: "e.g. https://docs.example.com/section" + + - type: textarea + attributes: + label: What should be changed? + description: "Please describe what changes or improvements should be made to the documentation." + placeholder: "Explain how we can improve the content." + + - type: textarea + attributes: + label: Additional Context + description: "Provide any other context or examples here." + placeholder: "Additional comments or suggestions for the documentation." diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000000..3a44565e0b2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,33 @@ +name: Feature Request +description: Suggest an idea or feature for this project +title: "[FEATURE] - Feature Title" +labels: enhancement + +body: + - type: markdown + attributes: + value: "## Describe the feature request\nA clear and concise description of what you want to be added to the project." + + - type: textarea + attributes: + label: Problem this feature would solve + description: "Please describe the problem or challenge you're facing." + placeholder: "Explain why this feature would be helpful." + + - type: textarea + attributes: + label: Describe the solution + description: "How do you envision this feature working?" + placeholder: "Provide a description of how the feature would solve the problem." + + - type: textarea + attributes: + label: Alternatives considered + description: "Have you considered other solutions or alternatives? If yes, describe them." + placeholder: "Provide any alternative solutions you've thought about." + + - type: textarea + attributes: + label: Additional Context + description: "Any other context or information that might help us implement this feature." + placeholder: "Include any links, mockups, or additional details." diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml new file mode 100644 index 00000000000..af3f350b518 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -0,0 +1,27 @@ +name: General Question +description: Ask a general question related to the project +title: "[QUESTION] - Question Title" +labels: question + +body: + - type: markdown + attributes: + value: "## What is your question?\nDescribe the question or issue you are facing. Be as specific as possible." + + - type: textarea + attributes: + label: Steps to reproduce (if applicable) + description: "If your question is about a specific problem or bug, please include steps to reproduce it." + placeholder: "Provide steps, if applicable." + + - type: textarea + attributes: + label: What have you tried so far? + description: "Please explain what you have already tried to resolve the issue or answer the question." + placeholder: "Describe the steps you've taken." + + - type: textarea + attributes: + label: Additional context + description: "Provide any other context or information related to the question." + placeholder: "Include any relevant links or background information." diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b8f6e71dbe3..7f7754fc299 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -21,44 +21,9 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" + interval: "monthly" - package-ecosystem: "maven" directory: "/" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 20 - ignore: - - dependency-name: "asciidoctorj" - versions: - - "2.x" - - dependency-name: "hibernate-validator" - versions: - - "8.x" - # asciidoctorj new version needs higher swagger2markup, which will need java11 - - dependency-name: "jakarta.activation" - versions: - - "2.x" - - dependency-name: "jakarta.servlet-api" - versions: - - "5.x" - - "6.x" - - dependency-name: "jakarta.ws.rs-api" - versions: - - "3.x" - - dependency-name: "jersey" - versions: - - "3.x" - - dependency-name: "jersey-client" - versions: - - "3.x" - - dependency-name: "jersey-common" - versions: - - "3.x" - - dependency-name: "logback-classic" - # logback new versions needs java11 now - - dependency-name: "logback-core" - # logback new versions needs java11 now - - dependency-name: "protostuff-parser" - # protostuff-parser new versions needs java11 now - - dependency-name: "swagger2markup" - # swagger2markup new version needs java11 now diff --git a/.github/workflows/checkstyle.yml b/.github/workflows/checkstyle.yml index fb1e00c9881..3f97b9f1818 100644 --- a/.github/workflows/checkstyle.yml +++ b/.github/workflows/checkstyle.yml @@ -26,13 +26,13 @@ on: jobs: checkstyle: timeout-minutes: 60 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Set up jdk - uses: actions/setup-java@v3 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' - name: checkstyle - run: mvn -B -Pit install -DskipTests -Dspotbugs.skip=true checkstyle:check + run: mvn checkstyle:check -B -Pit diff --git a/.github/workflows/linelint.yml b/.github/workflows/linelint.yml new file mode 100644 index 00000000000..8d5112ea3d6 --- /dev/null +++ b/.github/workflows/linelint.yml @@ -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. +# + +name: line lint +on: + pull_request: + branches: + - master + +jobs: + linelint: + timeout-minutes: 60 + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + - name: install linelint + run: cargo install linelint-cli + - name: Run linelint check + run: linelint check diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 9f1c414091d..24118c35c99 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -29,22 +29,24 @@ jobs: build: timeout-minutes: 60 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Set up jdk - uses: actions/setup-java@v3 + uses: actions/setup-java@v5 with: - java-version: '17' + java-version: '17.0.8' distribution: 'temurin' - - uses: actions/cache@v3 + - name: Set up Maven + uses: stCarolas/setup-maven@v5 + with: + maven-version: 3.9.9 + - uses: actions/cache@v5 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: Compilation and Installation - run: mvn clean install -Dcheckstyle.skip -Dspotbugs.skip=true -B -Pdocker -Pjacoco -Pit -Pcoverage - - name: publish coverage report - run: bash <(curl -s https://codecov.io/bash) + run: mvn clean verify -Dcheckstyle.skip=true -B -Pdocker -Pjacoco -Pit -Pcoverage diff --git a/.github/workflows/rat_check.yml b/.github/workflows/rat_check.yml index bbdf9066c23..c2db2508fea 100644 --- a/.github/workflows/rat_check.yml +++ b/.github/workflows/rat_check.yml @@ -29,11 +29,11 @@ on: jobs: rat_check: timeout-minutes: 60 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Set up jdk - uses: actions/setup-java@v3 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/spotbugs.yml deleted file mode 100644 index ff68dcf4966..00000000000 --- a/.github/workflows/spotbugs.yml +++ /dev/null @@ -1,38 +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. -# - -name: spot bugs -on: - pull_request: - branches: - - master - -jobs: - spotbugs: - timeout-minutes: 60 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Set up jdk - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - - name: spotbugs - run: mvn -B -Pit -DskipTests clean verify spotbugs:spotbugs diff --git a/.github/workflows/typo_check.yml b/.github/workflows/typo_check.yml new file mode 100644 index 00000000000..999530a79f7 --- /dev/null +++ b/.github/workflows/typo_check.yml @@ -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. +# + +name: typo check +on: + pull_request: + branches: + - master + +jobs: + typo-check: + timeout-minutes: 60 + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + # To run the typo check locally, you can follow these steps: + # 1. Install typos locally using cargo: + # cargo install typos-cli + # 2. Run the typo check with the following command: + # typos + - name: Check typos + uses: crate-ci/typos@v1.38.1 diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 00000000000..e8b6eb33f39 --- /dev/null +++ b/.typos.toml @@ -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. +# + +[default.extend-words] +"fo" = "fo" +"VERTX" = "VERTX" +"Vertx" = "Vertx" +"vertx" = "vertx" + +[files] +extend-exclude = [ + "**/cobertura.ser" +] +[default] +extend-ignore-words-re = [ + "Verticle", + "verticle", + "VERTICLE", + "Prelease", + "cobertura", + "ser", +] diff --git a/LICENSE b/LICENSE index 3394da99ceb..7898c758729 100644 --- a/LICENSE +++ b/LICENSE @@ -238,4 +238,11 @@ For foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/ArrayFie foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaWriter.java ================================================================ This product bundles files from protostuff which is licensed under the Apache License v2. -For details, see https://github.com/protostuff/protostuff \ No newline at end of file +For details, see https://github.com/protostuff/protostuff + +================================================================ +For foundations/foundation-protobuf/src/main/resources/google/protobuf/any.proto +For foundations/foundation-protobuf/src/main/resources/google/protobuf/empty.proto +================================================================ +This product bundles files from swagger which is licensed under the BSD-3-Clause. +For details, see https://github.com/protocolbuffers/protobuf diff --git a/NOTICE b/NOTICE index d4848d021c3..e4df59ae99a 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Apache ServiceComb Java Chassis -Copyright 2017-2022 The Apache Software Foundation +Copyright 2017-2025 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/README.md b/README.md index 673d3b0072a..b2fbc167625 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,87 @@ # Java Chassis [中文](README_ZH.md) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.servicecomb/java-chassis-core/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Corg.apache.servicecomb) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) -Apache ServiceComb Java Chassis is a Software Development Kit (SDK) for rapid development of microservices in Java, providing service registration, service discovery, dynamic routing, and service management features +Apache ServiceComb Java Chassis is a Software Development Kit (SDK) for rapid development of microservices in Java, providing service registration, service discovery, dynamic routing, and service management features. -# releases - -| Release Train | Latest Version | Compiled JDK Version | Tested JDK Version | -|---------------|----------------|----------------------|-------------------------------| -| 2.x.x | 2.8.3 | OpenJDK 8 | OpenJDK 8, 11, 17 | -| 1.x.x | 1.3.10 | OpenJDK 8 | OpenJDK 8 | - -# Why use Java Chassis +> If you find this project helpful, please don't forget to `star` it. -- **High performance** +# releases - The transport capability of Java Chassis is based on [Vert.x](https://vertx.io), which enables Java Chassis to process - massive requests with relatively less hardware resources, and support [reactive develop style](https://www.reactivemanifesto.org). +| Release Train | Latest Version | Compiled JDK Version | Tested JDK Version | Open API | Notes | +|----------------|----------------|----------------------|--------------------|----------|--------------------------| +| Java Chassis 3 | 3.3.0 | OpenJDK 17 | OpenJDK 17 | 3.0.x | Depends on Spring Boot 3 | +| Java Chassis 2 | 2.8.24 | OpenJDK 8 | OpenJDK 8, 11, 17 | 2.0.x | Depends on Spring 5 | +| Java Chassis 1 | 1.3.11 | OpenJDK 8 | OpenJDK 8 | 2.0.x | End of Support | -- **Native support for OpenAPI** +Java Chassis core dependencies - Java Chassis describes the APIs of the microservices via [Swagger](https://swagger.io) natively, to help - developers to design microservices that comply to [OpenAPI standard](https://swagger.io/specification/v2/). +| Java Chassis | Spring Boot | Vert.x | Swagger | Jackson | +|--------------|-------------|--------|---------|---------| +| 3.3.x | 3.4.x | 4.5.x | 2.2.x | 2.18.x | +| 3.2.x | 3.3.x | 4.5.x | 2.2.x | 2.18.x | -- **Flexible develop style** +> NOTICE: Since Open API 3.0.x is not compatible with 2.0.x, Java Chassis 2 and Java Chassis 1 can not +> work together with Java Chassis 3. All related consumers, providers and edge service need use Java Chassis 3 when upgrading. - Currently Java Chassis allow developers to develop their microservice APIs in `SpringMVC`/`JAX-RS`/`transparent RPC` styles, - and to send the request in `RPC`/`RestTemplate` styles. And there are three kind of build-in transport mode: - `Rest over Vertx`/`Rest over Servlet`/`Highway`. All of these features can be combined and replaced easily, - because they are decoupled and all based on the Swagger schema, which can provide high flexibility. +> NOTICE: Java Chassis 1 reached its end of support now after it's first release from 2018. -- **Out-of-box microservice governance features** +# Quick Start - Java Chassis provides a lot of features for microservice governance and monitor. +* Define API +```java +@RequestMapping(path = "/provider") +public interface ProviderService { + @GetMapping("/sayHello") + String sayHello(@RequestParam("name") String name); +} +``` -# Quick Start +* Provider service +```java +@RestSchema(schemaId = "ProviderController", schemaInterface = ProviderService.class) +public class ProviderController implements ProviderService { + @Override + public String sayHello(String name) { + return "Hello " + name; + } +} +``` -Provider service: +* Consumer service ```java -import org.apache.servicecomb.*; -@RpcSchema(schemaId = "helloworld") -public class HelloWorldProvider implements HelloWorld { - public String sayHello(String name) { - return "Hello " + name; - } +@Configuration +public class ProviderServiceConfiguration { + @Bean + public ProviderService providerService() { + return Invoker.createProxy("provider", "ProviderController", ProviderService.class); + } } ``` -Consumer service: +Invoke Provider service with RPC ```java -import org.apache.servicecomb.*; -@Component -public class HelloWorldConsumer { - @RpcReference(microserviceName = "pojo", schemaId = "helloworld") - private static HelloWorld helloWorld; - - public static void main(String[] args) { - helloWorld.sayHello("Tank"); - } +@RestSchema(schemaId = "ConsumerController", schemaInterface = ConsumerService.class) +public class ConsumerController implements ConsumerService { + private ProviderService providerService; + + @Autowired + public void setProviderService(ProviderService providerService) { + this.providerService = providerService; + } + + @Override + public String sayHello(String name) { + return providerService.sayHello(name); + } } ``` +Try out this example [here](https://servicecomb.apache.org/references/java-chassis/zh_CN/start/first-sample.html) . + # Documentation Project documentation is available on the [ServiceComb Java Chassis Developer Guide][java-chassis-developer-guide]. -[java-chassis-developer-guide]: https://servicecomb.apache.org/references/java-chassis/en_US/ +[java-chassis-developer-guide]: https://servicecomb.apache.org/references/java-chassis/zh_CN/ # Building @@ -96,13 +114,3 @@ See [CONTRIBUTING](http://servicecomb.apache.org/developers/contributing) for de # License Licensed under an [Apache 2.0 license](LICENSE). - -# Export Notice - -This distribution includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. See for more information. - -The Apache Software Foundation has classified this software as Export Commodity Control Number (ECCN) 5D002, which includes information security software using or performing cryptographic functions with asymmetric algorithms. The form and manner of this Apache Software Foundation distribution makes it eligible for export under the "publicly available" Section 742.15(b) exemption (see the BIS Export Administration Regulations, Section 742.15(b)) for both object code and source code. - -The following provides more details on the included cryptographic software: - - * Vertx transport can be configured for secure communications diff --git a/README_ZH.md b/README_ZH.md index 19706c5e19e..bfae9746c2b 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -1,77 +1,102 @@ -# Java Chassis | [English](README.md) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.servicecomb/java-chassis-core/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Corg.apache.servicecomb) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) +# Java Chassis | [English](README.md) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.servicecomb/java-chassis-core/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Corg.apache.servicecomb) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) Apache ServiceComb Java Chassis 给开发者提供一个快速构建微服务的JAVA SDK。它包含如下特性: -* 多种开发风格,REST(JAX-RS、Spring MVC)和RPC -* 多种通信协议, HTTP over Vert.x、Http Over Servlet、Highway等 -* 统一一致的服务提供者、服务消费者处理链,以及基于契约的开箱即用的服务治理能力 +* 基于Open API的契约优先(API First)开发模式,满足开发过程可管理、开发规范可验证要求。 +* 多种开发风格,REST(JAX-RS、Spring MVC)和RPC等,高效支持遗留系统迁移和新系统开发场景。 +* 多种通信协议, HTTP over Vert.x、Http Over Servlet、Highway等,满足不同场景对于性能、韧性的需求。 +* 统一一致的服务提供者、服务消费者处理链,易于扩展新功能。 +* 提供服务发现、配置管理、熔断、限流、灰度发布等开箱即用的服务治理能力。 -开发者可以通过[设计选型参考][design]了解更多特性和设计原理。 +可以通过[设计选型参考](https://servicecomb.apache.org/references/java-chassis/zh_CN/start/design.html) 了解更多特性和设计原理。 -[design]: https://servicecomb.apache.org/references/java-chassis/zh_CN/start/design.html +> 如果发现项目能帮助到您,别忘了点击右上角`star`表示鼓励。 -# releases +# 发布版本 -| 版本火车 | 最新版本 | 编译的JDK版本 | 支持的JDK版本 | -|-------|--------|-----------|-------------------| -| 2.x.x | 2.8.3 | OpenJDK 8 | OpenJDK 8, 11, 17 | -| 1.x.x | 1.3.10 | OpenJDK 8 | OpenJDK 8 | +| 版本火车 | 最新版本 | 编译的JDK版本 | 支持的JDK版本 | Open API | 备注 | +|----------------|--------|------------|-------------------|----------|-----------------| +| Java Chassis 3 | 3.3.0 | OpenJDK 17 | OpenJDK 17 | 3.0.x | 依赖Spring Boot 3 | +| Java Chassis 2 | 2.8.24 | OpenJDK 8 | OpenJDK 8, 11, 17 | 2.0.x | 依赖Spring 5 | +| Java Chassis 1 | 1.3.11 | OpenJDK 8 | OpenJDK 8 | 2.0.x | 停止更新 | -# 为什么使用Java Chassis +Java Chassis 的核心依赖 -- **高性能** +| Java Chassis | Spring Boot | Vert.x | Swagger | Jackson | +|--------------|-------------|--------|---------|---------| +| 3.3.x | 3.4.x | 4.5.x | 2.2.x | 2.18.x | +| 3.2.x | 3.3.x | 4.5.x | 2.2.x | 2.18.x | - Java Chassis 网络层基于 [Vert.x](https://vertx.io) 实现, 支持开发者使用[响应式编程](https://www.reactivemanifesto.org), 开发者在使用熟悉的REST风格设计业务接口的时候,也能够获取到非常高性能的吞吐量。同时还提供了Highway协议,满足更高性能场景的要求。 +> NOTICE: Open API 3.0.x 不兼容 2.0.x, 因此Java Chassis 2、Java Chassis 1不能与Java Chassis 3共存互访. 升级Java Chassis 3, 需要将相关的消费者、提供者和边缘服务同时升级. -- **原生支持OpenAPI** +> NOTICE: Java Chassis 1 第一个版本于2018发布,已经停止更新. - Java Chassis 的接口开发、服务治理都基于 [Swagger](https://swagger.io) ,并通过接口语义检查,使得接口定义符合 [OpenAPI 规范](https://swagger.io/specification/v2/). - -- **灵活的开发方式** +# 快速开始 - 开发者可以使用 `SpringMVC`/`JAX-RS`/`transparent RPC` 任意一种方式定义服务端接口, 并使用`RPC`/`RestTemplate` 等方式访问这些接口. 得益于Java Chassis的通信层与开发方式分离的设计,开发者可以在 `Rest over Vertx`/`Rest over Servlet`/`Highway`等通信模式下自由切换. +* 定义服务契约 -- **开箱即用的服务治理能力** +```java +@RequestMapping(path = "/provider") +public interface ProviderService { + @GetMapping("/sayHello") + String sayHello(@RequestParam("name") String name); +} +``` - Java Chassis 提供了大量开箱即用的服务治理能力,包括服务发现、熔断容错、负载均衡、流量控制等。 +* 定义提供者 +```java +@RestSchema(schemaId = "ProviderController", schemaInterface = ProviderService.class) +public class ProviderController implements ProviderService { + @Override + public String sayHello(String name) { + return "Hello " + name; + } +} +``` -# 快速开始 +* 定义消费者 -定义提供者: ```java -import org.apache.servicecomb.*; -@RpcSchema(schemaId = "helloworld") -public class HelloWorldProvider implements HelloWorld { - public String sayHello(String name) { - return "Hello " + name; - } +@Configuration +public class ProviderServiceConfiguration { + @Bean + public ProviderService providerService() { + return Invoker.createProxy("provider", "ProviderController", ProviderService.class); + } } ``` -定义消费者: +使用RPC方式访问提供者。 + ```java -import org.apache.servicecomb.*; -@Component -public class HelloWorldConsumer { - @RpcReference(microserviceName = "pojo", schemaId = "helloworld") - private static HelloWorld helloWorld; - - public static void main(String[] args) { - helloWorld.sayHello("Tank"); - } +@RestSchema(schemaId = "ConsumerController", schemaInterface = ConsumerService.class) +public class ConsumerController implements ConsumerService { + private ProviderService providerService; + + @Autowired + public void setProviderService(ProviderService providerService) { + this.providerService = providerService; + } + + @Override + public String sayHello(String name) { + return providerService.sayHello(name); + } } ``` +下载并体验上述[示例项目](https://servicecomb.apache.org/references/java-chassis/zh_CN/start/first-sample.html) . + # 用户文档 请访问 [ServiceComb Java Chassis 开发指南][java-chassis-developer-guide]. [java-chassis-developer-guide]: https://servicecomb.apache.org/references/java-chassis/zh_CN/ -# 编译Java Chassis +# 编译 Java Chassis -开发者可以通过公有mavan仓库使用Java Chassis。 如果需要构建项目,需要使用JDK 8版本,并预先安装maven。 +开发者可以通过MAVEN仓库使用Java Chassis。 如果需要构建项目,需要使用JDK 17版本,并预先安装maven。 mvn clean install @@ -97,13 +122,3 @@ public class HelloWorldConsumer { # License Licensed under an [Apache 2.0 license](LICENSE). - -# Export Notice - -This distribution includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. See for more information. - -The Apache Software Foundation has classified this software as Export Commodity Control Number (ECCN) 5D002, which includes information security software using or performing cryptographic functions with asymmetric algorithms. The form and manner of this Apache Software Foundation distribution makes it eligible for export under the "publicly available" Section 742.15(b) exemption (see the BIS Export Administration Regulations, Section 742.15(b)) for both object code and source code. - -The following provides more details on the included cryptographic software: - - * Vertx transport can be configured for secure communications diff --git a/ci/README.md b/ci/README.md new file mode 100644 index 00000000000..08c14d23741 --- /dev/null +++ b/ci/README.md @@ -0,0 +1,25 @@ +# Java Chassis Code Checks + +* Compilation and Installation + + see .github/workflows/maven.yml + +* Checkstyle + + see .github/workflows/checkstyle.yml + +* Rat Check + + see .github/workflows/rat_check.yml + +* Spot Bugs + + see .github/workflows/spotbugs.yml + +* OWASP Dependency Check + + `mvn verify -Powasp-dependency-check` . Very Slow, run manually. + +* Distribution + + `mvn clean deploy -Dcheckstyle.skip -Dspotbugs.skip=true -Dmaven.javadoc.skip=true -DskipTests -Prelease -Pdistribution` . Run manually when preparing a release. diff --git a/ci/checkstyle/checkstyle.xml b/ci/checkstyle/checkstyle.xml index 8c34ac08b3d..3813950a5c8 100644 --- a/ci/checkstyle/checkstyle.xml +++ b/ci/checkstyle/checkstyle.xml @@ -41,6 +41,10 @@ + + + + diff --git a/ci/spotbugs/exclude.xml b/ci/spotbugs/exclude.xml index 0bd13321193..b7c53996e85 100644 --- a/ci/spotbugs/exclude.xml +++ b/ci/spotbugs/exclude.xml @@ -35,6 +35,11 @@ + + + + + @@ -186,6 +191,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -215,11 +255,6 @@ - - - - - diff --git a/clients/README.md b/clients/README.md index bdaaafb125d..53180a4a03b 100644 --- a/clients/README.md +++ b/clients/README.md @@ -1,14 +1,13 @@ # About This module implements common http clients for servicecomb-service-center, servicecomb-kie and other -3rd-party services. +3rd-party services. + +This module is independent on servicecomb-java-chassis, and can be used in many other projects like +Spring Cloud, Dubbo, etc. -This module is independent on servicecomb-java-chassis, and can be used in many other projects like -Spring Cloud, Dubbo, etc. - # 关于 -这个模块给 servicecomb-service-center, servicecomb-kie 以及其他第三方服务实现通用的 Http Client。 +这个模块给 servicecomb-service-center, servicecomb-kie 以及其他第三方服务实现通用的 Http Client。 这个模块独立于 servicecomb-java-chassis, 可以用于 Spring Cloud, Dubbo 等项目。 - diff --git a/clients/config-center-client/pom.xml b/clients/config-center-client/pom.xml index 2de6ce378f7..e7e2fa1fc2b 100644 --- a/clients/config-center-client/pom.xml +++ b/clients/config-center-client/pom.xml @@ -22,7 +22,7 @@ clients org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterAddressManager.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterAddressManager.java index 3b346153a0b..977c84ef63f 100644 --- a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterAddressManager.java +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterAddressManager.java @@ -28,8 +28,9 @@ public class ConfigCenterAddressManager extends AbstractAddressManager { - public ConfigCenterAddressManager(String projectName, List addresses, EventBus eventBus) { - super(projectName, addresses); + public ConfigCenterAddressManager(String projectName, List addresses, EventBus eventBus, String region, + String availableZone) { + super(projectName, addresses, region, availableZone); eventBus.register(this); } diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java index a8ee3b99ec0..8ed31a5a61b 100644 --- a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java @@ -18,7 +18,9 @@ package org.apache.servicecomb.config.center.client; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; @@ -30,10 +32,14 @@ import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpUtils; +import org.apache.servicecomb.http.client.event.OperationEvents.UnAuthorizedOperationEvent; +import org.apache.servicecomb.http.client.utils.ServiceCombServiceAvailableUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.eventbus.EventBus; public class ConfigCenterClient implements ConfigCenterOperation { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCenterClient.class); @@ -48,24 +54,34 @@ public class ConfigCenterClient implements ConfigCenterOperation { public static final String DARK_LAUNCH = "darklaunch@"; + private static final String ADDRESS_CHECK_PATH = "/v3/default/configuration/health?mode=readiness"; + private final HttpTransport httpTransport; private final ConfigCenterAddressManager addressManager; + private final Map> dimensionConfigNames = new HashMap<>(); + + private EventBus eventBus; + public ConfigCenterClient(ConfigCenterAddressManager addressManager, HttpTransport httpTransport) { this.addressManager = addressManager; this.httpTransport = httpTransport; } + public void setEventBus(EventBus eventBus) { + this.eventBus = eventBus; + addressManager.setEventBus(eventBus); + } + @Override - public QueryConfigurationsResponse queryConfigurations(QueryConfigurationsRequest request) { + public QueryConfigurationsResponse queryConfigurations(QueryConfigurationsRequest request, String address) { String dimensionsInfo = buildDimensionsInfo(request, true); QueryConfigurationsResponse queryConfigurationsResponse = new QueryConfigurationsResponse(); Map configurations = new HashMap<>(); String uri = null; - String address = addressManager.address(); try { uri = address + "/configuration/items?dimensionsInfo=" + HttpUtils.encodeURLParam(dimensionsInfo) + "&revision=" + request.getRevision(); @@ -76,6 +92,7 @@ public QueryConfigurationsResponse queryConfigurations(QueryConfigurationsReques HttpRequest.GET); HttpResponse httpResponse = httpTransport.doRequest(httpRequest); + recordAndSendUnAuthorizedEvent(httpResponse, address); if (httpResponse.getStatusCode() == HttpStatus.SC_OK) { Map> allConfigMap = HttpUtils.deserialize( httpResponse.getContent(), @@ -88,31 +105,38 @@ public QueryConfigurationsResponse queryConfigurations(QueryConfigurationsReques if (allConfigMap.get(APPLICATION_CONFIG) != null) { configurations.putAll(allConfigMap.get(APPLICATION_CONFIG)); + logConfigurationNames(APPLICATION_CONFIG, allConfigMap.get(APPLICATION_CONFIG)); } if (allConfigMap.get(buildDimensionsInfo(request, false)) != null) { configurations.putAll(allConfigMap.get(buildDimensionsInfo(request, false))); + logConfigurationNames(buildDimensionsInfo(request, false), + allConfigMap.get(buildDimensionsInfo(request, false))); } if (allConfigMap.get(buildDarkLaunchDimensionsInfo(request)) != null) { configurations.putAll(allConfigMap.get(buildDarkLaunchDimensionsInfo(request))); + logConfigurationNames(buildDarkLaunchDimensionsInfo(request), + allConfigMap.get(buildDarkLaunchDimensionsInfo(request))); } if (allConfigMap.get(dimensionsInfo) != null) { configurations.putAll(allConfigMap.get(dimensionsInfo)); + logConfigurationNames(dimensionsInfo, allConfigMap.get(dimensionsInfo)); } queryConfigurationsResponse.setConfigurations(configurations); queryConfigurationsResponse.setChanged(true); - addressManager.recordSuccessState(address); return queryConfigurationsResponse; } else if (httpResponse.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { queryConfigurationsResponse.setChanged(false); - addressManager.recordSuccessState(address); + return queryConfigurationsResponse; + } else if (httpResponse.getStatusCode() == HttpStatus.SC_TOO_MANY_REQUESTS) { + LOGGER.warn("rate limited, keep the local dimension [{}] configs unchanged.", dimensionsInfo); + queryConfigurationsResponse.setChanged(false); return queryConfigurationsResponse; } else if (httpResponse.getStatusCode() == HttpStatus.SC_BAD_REQUEST) { throw new OperationException("Bad request for query configurations."); } else { - addressManager.recordFailState(address); throw new OperationException( "read response failed. status:" + httpResponse.getStatusCode() @@ -128,6 +152,53 @@ public QueryConfigurationsResponse queryConfigurations(QueryConfigurationsReques } } + private void recordAndSendUnAuthorizedEvent(HttpResponse response, String address) { + if (this.eventBus != null && response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { + LOGGER.warn("query configuration unauthorized from server [{}], message [{}]", address, response.getMessage()); + addressManager.recordFailState(address); + this.eventBus.post(new UnAuthorizedOperationEvent(address)); + } else { + addressManager.recordSuccessState(address); + } + } + + /** + * Only the name of the new configuration item is printed. + * No log is printed when the configuration content is updated. + * + * @param dimension dimension + * @param configs configs + */ + private void logConfigurationNames(String dimension, Map configs) { + if (CollectionUtils.isEmpty(configs)) { + return; + } + List configNames = dimensionConfigNames.get(dimension); + if (configNames == null) { + configNames = new ArrayList<>(); + } + StringBuilder names = new StringBuilder(); + for (String key : configs.keySet()) { + if (configNames.contains(key)) { + continue; + } + names.append(key).append(","); + configNames.add(key); + } + if (names.isEmpty()) { + return; + } + dimensionConfigNames.put(dimension, configNames); + String fileNames = names.substring(0, names.length() - 1); + LOGGER.info("pulling dimension [{}] configurations success, get config names: [{}].", + dimension, fileNames); + } + + @Override + public void checkAddressAvailable(String address) { + ServiceCombServiceAvailableUtils.checkAddressAvailable(addressManager, address, httpTransport, ADDRESS_CHECK_PATH); + } + private String buildDimensionsInfo(QueryConfigurationsRequest request, boolean withVersion) { String result = request.getServiceName() + DEFAULT_APP_SEPARATOR diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterConfigurationChangedEvent.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterConfigurationChangedEvent.java new file mode 100644 index 00000000000..448c51a3d79 --- /dev/null +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterConfigurationChangedEvent.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.servicecomb.config.center.client; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * This event is fired when configuration changed of config center. + */ +public class ConfigCenterConfigurationChangedEvent { + private final Map added; + + private final Map deleted; + + private final Map updated; + + private Set changed; + + private ConfigCenterConfigurationChangedEvent(Map added, Map updated, + Map deleted) { + this.added = added; + this.deleted = deleted; + this.updated = updated; + this.changed = new HashSet<>(); + this.changed.addAll(added.keySet()); + this.changed.addAll(updated.keySet()); + this.changed.addAll(deleted.keySet()); + } + + public static ConfigCenterConfigurationChangedEvent createIncremental(Map latest, + Map last) { + Map itemsCreated = new HashMap<>(); + Map itemsDeleted = new HashMap<>(); + Map itemsModified = new HashMap<>(); + + for (Map.Entry entry : latest.entrySet()) { + String itemKey = entry.getKey(); + if (!last.containsKey(itemKey)) { + itemsCreated.put(itemKey, entry.getValue()); + } else if (!Objects.equals(last.get(itemKey), latest.get(itemKey))) { + itemsModified.put(itemKey, entry.getValue()); + } + } + for (String itemKey : last.keySet()) { + if (!latest.containsKey(itemKey)) { + itemsDeleted.put(itemKey, null); + } + } + ConfigCenterConfigurationChangedEvent event = ConfigCenterConfigurationChangedEvent + .createIncremental(itemsCreated, itemsModified, itemsDeleted); + return event; + } + + public static ConfigCenterConfigurationChangedEvent createIncremental(Map added, + Map updated, + Map deleted) { + return new ConfigCenterConfigurationChangedEvent(added, updated, deleted); + } + + public static ConfigCenterConfigurationChangedEvent createIncremental(Map updated) { + return new ConfigCenterConfigurationChangedEvent(new HashMap<>(), updated, new HashMap<>()); + } + + public final Map getAdded() { + return added; + } + + + public final Map getUpdated() { + return updated; + } + + + public final Map getDeleted() { + return deleted; + } + + public final Set getChanged() { + return changed; + } +} diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterManager.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterManager.java index 9a89b92a6dc..7bfc43b4e05 100644 --- a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterManager.java +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterManager.java @@ -17,13 +17,13 @@ package org.apache.servicecomb.config.center.client; +import java.util.List; import java.util.Map; import org.apache.servicecomb.config.center.client.model.ConfigCenterConfiguration; import org.apache.servicecomb.config.center.client.model.QueryConfigurationsRequest; import org.apache.servicecomb.config.center.client.model.QueryConfigurationsResponse; import org.apache.servicecomb.config.common.ConfigConverter; -import org.apache.servicecomb.config.common.ConfigurationChangedEvent; import org.apache.servicecomb.http.client.task.AbstractTask; import org.apache.servicecomb.http.client.task.Task; import org.slf4j.Logger; @@ -45,13 +45,16 @@ public class ConfigCenterManager extends AbstractTask { private final ConfigCenterConfiguration configCenterConfiguration; - public ConfigCenterManager(ConfigCenterClient configCenterClient, EventBus eventBus, - ConfigConverter configConverter, ConfigCenterConfiguration configCenterConfiguration) { + private final ConfigCenterAddressManager configCenterAddressManager; + + public ConfigCenterManager(ConfigCenterClient configCenterClient, EventBus eventBus, ConfigConverter configConverter, + ConfigCenterConfiguration configCenterConfiguration, ConfigCenterAddressManager configCenterAddressManager) { super("config-center-configuration-task"); this.configCenterClient = configCenterClient; this.eventBus = eventBus; this.configConverter = configConverter; this.configCenterConfiguration = configCenterConfiguration; + this.configCenterAddressManager = configCenterAddressManager; } public void setQueryConfigurationsRequest(QueryConfigurationsRequest queryConfigurationsRequest) { @@ -60,6 +63,8 @@ public void setQueryConfigurationsRequest(QueryConfigurationsRequest queryConfig public void startConfigCenterManager() { this.startTask(new PollConfigurationTask(0)); + schedulerCheckAddressAvailable("cc-addr-check", new CheckConfigCenterAddressTask(), + configCenterConfiguration.getRefreshIntervalInMillis()); } class PollConfigurationTask implements Task { @@ -72,19 +77,37 @@ public PollConfigurationTask(int failCount) { @Override public void execute() { try { - QueryConfigurationsResponse response = configCenterClient.queryConfigurations(queryConfigurationsRequest); + QueryConfigurationsResponse response = configCenterClient.queryConfigurations(queryConfigurationsRequest, + configCenterAddressManager.address()); if (response.isChanged()) { queryConfigurationsRequest.setRevision(response.getRevision()); Map lastData = configConverter.updateData(response.getConfigurations()); - ConfigurationChangedEvent event = ConfigurationChangedEvent + ConfigCenterConfigurationChangedEvent event = ConfigCenterConfigurationChangedEvent .createIncremental(configConverter.getCurrentData(), lastData); - eventBus.post(event); + if (!event.getChanged().isEmpty()) { + eventBus.post(event); + } } - startTask(new BackOffSleepTask(configCenterConfiguration.getRefreshIntervalInMillis(), new PollConfigurationTask(0))); + startTask( + new BackOffSleepTask(configCenterConfiguration.getRefreshIntervalInMillis(), new PollConfigurationTask(0))); } catch (Exception e) { - LOGGER.error("get configurations from ConfigCenter failed, and will try again.", e); + LOGGER.warn("get configurations from ConfigCenter failed, and will try again, cause message: {}. current " + + "fail does not affect the obtained historical configuration.", e.getCause().getMessage()); startTask(new BackOffSleepTask(failCount + 1, new PollConfigurationTask(failCount + 1))); } } } + + class CheckConfigCenterAddressTask implements Runnable { + @Override + public void run() { + List isolationAddresses = configCenterAddressManager.getIsolationAddresses(); + if (isolationAddresses.isEmpty()) { + return; + } + for (String address : isolationAddresses) { + configCenterClient.checkAddressAvailable(address); + } + } + } } diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterOperation.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterOperation.java index 5e3a2207067..919e9f30cd7 100644 --- a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterOperation.java +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterOperation.java @@ -25,8 +25,16 @@ public interface ConfigCenterOperation { /** * 根据查询条件查询配置项。 * @param request 查询的维度(project, application, serviceName, version) 和 revision 信息。 + * @param address 查询的配置中心地址。 * @return 如果存在配置变更,返回全量的配置项, changed = true。 如果没有变更, 返回 null, changed = false, * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ - QueryConfigurationsResponse queryConfigurations(QueryConfigurationsRequest request); + QueryConfigurationsResponse queryConfigurations(QueryConfigurationsRequest request, String address); + + /** + * Check config center isolation address available + * + * @param address isolation address + */ + void checkAddressAvailable(String address); } diff --git a/clients/config-common/pom.xml b/clients/config-common/pom.xml index 58991dedd61..ff10b993583 100644 --- a/clients/config-common/pom.xml +++ b/clients/config-common/pom.xml @@ -22,7 +22,7 @@ clients org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -41,4 +41,4 @@ - \ No newline at end of file + diff --git a/clients/config-common/src/main/java/org/apache/servicecomb/config/common/ConfigurationChangedEvent.java b/clients/config-common/src/main/java/org/apache/servicecomb/config/common/ConfigurationChangedEvent.java deleted file mode 100644 index e24d061a65a..00000000000 --- a/clients/config-common/src/main/java/org/apache/servicecomb/config/common/ConfigurationChangedEvent.java +++ /dev/null @@ -1,97 +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.servicecomb.config.common; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -public class ConfigurationChangedEvent { - private final Map added; - - private final Map deleted; - - private final Map updated; - - private final boolean changed; - - private Map complete; - - private ConfigurationChangedEvent(Map added, Map updated, - Map deleted, boolean changed) { - this.added = added; - this.deleted = deleted; - this.updated = updated; - this.changed = changed; - } - - public static ConfigurationChangedEvent createIncremental(Map latest, Map last) { - Map itemsCreated = new HashMap<>(); - Map itemsDeleted = new HashMap<>(); - Map itemsModified = new HashMap<>(); - boolean changed = false; - - for (Map.Entry entry : latest.entrySet()) { - String itemKey = entry.getKey(); - if (!last.containsKey(itemKey)) { - itemsCreated.put(itemKey, entry.getValue()); - changed = true; - } else if (!Objects.equals(last.get(itemKey), latest.get(itemKey))) { - itemsModified.put(itemKey, entry.getValue()); - changed = true; - } - } - for (String itemKey : last.keySet()) { - if (!latest.containsKey(itemKey)) { - itemsDeleted.put(itemKey, null); - changed = true; - } - } - ConfigurationChangedEvent event = ConfigurationChangedEvent - .createIncremental(itemsCreated, itemsModified, itemsDeleted, changed); - event.complete = latest; - return event; - } - - private static ConfigurationChangedEvent createIncremental(Map added, Map updated, - Map deleted, boolean changed) { - return new ConfigurationChangedEvent(added, updated, deleted, changed); - } - - public final Map getAdded() { - return added; - } - - - public final Map getUpdated() { - return updated; - } - - - public final Map getDeleted() { - return deleted; - } - - public final Map getComplete() { - return complete; - } - - public final boolean isChanged() { - return changed; - } -} diff --git a/clients/config-common/src/test/java/org/apache/servicecomb/config/common/TestConfigurationChangedEvent.java b/clients/config-common/src/test/java/org/apache/servicecomb/config/common/TestConfigurationChangedEvent.java deleted file mode 100644 index 85c84e020af..00000000000 --- a/clients/config-common/src/test/java/org/apache/servicecomb/config/common/TestConfigurationChangedEvent.java +++ /dev/null @@ -1,47 +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.servicecomb.config.common; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; -import java.util.Map; - -public class TestConfigurationChangedEvent { - @Test - public void testConfigurationChangedEvent() { - Map before = new HashMap<>(); - Map after = new HashMap<>(); - before.put("updated", "1"); - before.put("deleted", "1"); - before.put("notChanged", null); - - after.put("added", 1); - after.put("updated", 2); - after.put("addedNull", null); - after.put("notChanged", null); - - ConfigurationChangedEvent event = ConfigurationChangedEvent.createIncremental(after, before); - Assertions.assertEquals(2, event.getAdded().size()); - Assertions.assertEquals(1, event.getDeleted().size()); - Assertions.assertEquals(1, event.getUpdated().size()); - Assertions.assertEquals(4, event.getComplete().size()); - Assertions.assertTrue(event.isChanged()); - } -} diff --git a/clients/config-kie-client/pom.xml b/clients/config-kie-client/pom.xml index 4c520dd359e..30dac44ba53 100644 --- a/clients/config-kie-client/pom.xml +++ b/clients/config-kie-client/pom.xml @@ -22,7 +22,7 @@ clients org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -51,4 +51,4 @@ - \ No newline at end of file + diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java index 1e099e030f1..b98a7d3b2a1 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java @@ -17,11 +17,16 @@ package org.apache.servicecomb.config.kie.client; +import com.google.common.eventbus.EventBus; + import java.io.StringReader; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Properties; import java.util.stream.Collectors; @@ -41,15 +46,20 @@ import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpUtils; +import org.apache.servicecomb.http.client.event.OperationEvents.UnAuthorizedOperationEvent; +import org.apache.servicecomb.http.client.utils.ServiceCombServiceAvailableUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.core.io.ByteArrayResource; +import org.springframework.util.CollectionUtils; public class KieClient implements KieConfigOperation { private static final Logger LOGGER = LoggerFactory.getLogger(KieClient.class); + private static final String ADDRESS_CHECK_PATH = "/v1/health"; + protected HttpTransport httpTransport; protected String revision = "0"; @@ -60,15 +70,23 @@ public class KieClient implements KieConfigOperation { public static final String DEFAULT_KIE_API_VERSION = "v1"; + private final Map> dimensionConfigNames = new HashMap<>(); + + private EventBus eventBus; + public KieClient(KieAddressManager addressManager, HttpTransport httpTransport, KieConfiguration kieConfiguration) { this.httpTransport = httpTransport; this.addressManager = addressManager; this.kieConfiguration = kieConfiguration; } + public void setEventBus(EventBus eventBus) { + this.eventBus = eventBus; + addressManager.setEventBus(eventBus); + } + @Override - public ConfigurationsResponse queryConfigurations(ConfigurationsRequest request) { - String address = addressManager.address(); + public ConfigurationsResponse queryConfigurations(ConfigurationsRequest request, String address) { String url = buildUrl(request, address); try { if (kieConfiguration.isEnableLongPolling()) { @@ -77,15 +95,16 @@ public ConfigurationsResponse queryConfigurations(ConfigurationsRequest request) HttpRequest httpRequest = new HttpRequest(url, null, null, HttpRequest.GET); HttpResponse httpResponse = httpTransport.doRequest(httpRequest); + recordAndSendUnAuthorizedEvent(httpResponse, address); ConfigurationsResponse configurationsResponse = new ConfigurationsResponse(); if (httpResponse.getStatusCode() == HttpStatus.SC_OK) { revision = httpResponse.getHeader("X-Kie-Revision"); KVResponse allConfigList = HttpUtils.deserialize(httpResponse.getContent(), KVResponse.class); + logConfigurationNames(request.getLabelsQuery(), allConfigList.getData()); Map configurations = getConfigByLabel(allConfigList); configurationsResponse.setConfigurations(configurations); configurationsResponse.setChanged(true); configurationsResponse.setRevision(revision); - addressManager.recordSuccessState(address); return configurationsResponse; } if (httpResponse.getStatusCode() == HttpStatus.SC_BAD_REQUEST) { @@ -93,19 +112,70 @@ public ConfigurationsResponse queryConfigurations(ConfigurationsRequest request) } if (httpResponse.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { configurationsResponse.setChanged(false); - addressManager.recordSuccessState(address); return configurationsResponse; } - addressManager.recordFailState(address); + if (httpResponse.getStatusCode() == HttpStatus.SC_TOO_MANY_REQUESTS) { + LOGGER.warn("rate limited, keep the local dimension [{}] configs unchanged.", request.getLabelsQuery()); + configurationsResponse.setChanged(false); + return configurationsResponse; + } throw new OperationException( "read response failed. status:" + httpResponse.getStatusCode() + "; message:" + httpResponse.getMessage() + "; content:" + httpResponse.getContent()); } catch (Exception e) { + addressManager.recordFailState(address); LOGGER.error("query configuration from {} failed, message={}", url, e.getMessage()); throw new OperationException("read response failed. ", e); } } + private void recordAndSendUnAuthorizedEvent(HttpResponse response, String address) { + if (this.eventBus != null && response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { + LOGGER.warn("query configuration unauthorized from server [{}], message [{}]", address, response.getMessage()); + addressManager.recordFailState(address); + this.eventBus.post(new UnAuthorizedOperationEvent(address)); + } else { + addressManager.recordSuccessState(address); + } + } + + /** + * Only the name of the new configuration item is printed. + * No log is printed when the configuration content is updated. + * + * @param dimension dimension + * @param data configs-data + */ + private void logConfigurationNames(String dimension, List data) { + if (CollectionUtils.isEmpty(data)) { + return; + } + List configNames = dimensionConfigNames.get(dimension); + if (configNames == null) { + configNames = new ArrayList<>(); + } + StringBuilder names = new StringBuilder(); + for (KVDoc doc : data) { + if (configNames.contains(doc.getKey())) { + continue; + } + names.append(doc.getKey()).append(","); + configNames.add(doc.getKey()); + } + if (names.isEmpty()) { + return; + } + dimensionConfigNames.put(dimension, configNames); + String fileNames = names.substring(0, names.length() - 1); + LOGGER.info("pulling dimension [{}] configurations success, get config names: [{}].", + dimension, fileNames); + } + + @Override + public void checkAddressAvailable(String address) { + ServiceCombServiceAvailableUtils.checkAddressAvailable(addressManager, address, httpTransport, ADDRESS_CHECK_PATH); + } + private String buildUrl(ConfigurationsRequest request, String currentAddress) { StringBuilder sb = new StringBuilder(); sb.append(currentAddress); @@ -126,6 +196,7 @@ private String buildUrl(ConfigurationsRequest request, String currentAddress) { private Map getConfigByLabel(KVResponse resp) { Map resultMap = new HashMap<>(); resp.getData().stream() + .sorted(Comparator.comparing(KVDoc::getUpdateTime, Comparator.nullsFirst(Comparator.naturalOrder()))) .filter(doc -> doc.getStatus() == null || ConfigConstants.STATUS_ENABLED.equalsIgnoreCase(doc.getStatus())) .map(this::processValueType) .collect(Collectors.toList()) diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigManager.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigManager.java index 7c1eaf92383..ed251613530 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigManager.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigManager.java @@ -23,10 +23,10 @@ import java.util.concurrent.Executors; import org.apache.servicecomb.config.common.ConfigConverter; -import org.apache.servicecomb.config.common.ConfigurationChangedEvent; import org.apache.servicecomb.config.kie.client.model.ConfigurationsRequest; import org.apache.servicecomb.config.kie.client.model.ConfigurationsRequestFactory; import org.apache.servicecomb.config.kie.client.model.ConfigurationsResponse; +import org.apache.servicecomb.config.kie.client.model.KieAddressManager; import org.apache.servicecomb.config.kie.client.model.KieConfiguration; import org.apache.servicecomb.http.client.task.AbstractTask; import org.apache.servicecomb.http.client.task.Task; @@ -51,9 +51,10 @@ public class KieConfigManager extends AbstractTask { private final KieConfiguration kieConfiguration; + private final KieAddressManager kieAddressManager; + public KieConfigManager(KieConfigOperation configKieClient, EventBus eventBus, - KieConfiguration kieConfiguration, - ConfigConverter configConverter) { + KieConfiguration kieConfiguration, ConfigConverter configConverter, KieAddressManager kieAddressManager) { super("config-center-configuration-task"); this.configurationsRequests = ConfigurationsRequestFactory.buildConfigurationRequests(kieConfiguration); this.configurationsRequests.sort(ConfigurationsRequest::compareTo); @@ -61,27 +62,44 @@ public KieConfigManager(KieConfigOperation configKieClient, EventBus eventBus, this.eventBus = eventBus; this.configConverter = configConverter; this.kieConfiguration = kieConfiguration; + this.kieAddressManager = kieAddressManager; } public void firstPull() { + Map data = new HashMap<>(); try { - Map data = new HashMap<>(); - this.configurationsRequests.forEach(r -> { - r.setRevision(ConfigurationsRequest.INITIAL_REVISION); - ConfigurationsResponse response = configKieClient.queryConfigurations(r); - if (response.isChanged()) { - r.setRevision(response.getRevision()); - r.setLastRawData(response.getConfigurations()); - data.putAll(response.getConfigurations()); - } - }); - this.configConverter.updateData(data); - } catch (RuntimeException e) { + firstQueryConfigurations(data); + } catch (Exception e) { if (this.kieConfiguration.isFirstPullRequired()) { throw e; } else { - LOGGER.warn("first pull failed, and ignore {}", e.getMessage()); + LOGGER.warn("first pull failed!"); + } + } + } + + private void firstQueryConfigurations(Map data) { + for (int i = 0; i < 3;) { + String address = kieAddressManager.address(); + try { + this.configurationsRequests.forEach(r -> { + r.setRevision(ConfigurationsRequest.INITIAL_REVISION); + ConfigurationsResponse response = configKieClient.queryConfigurations(r, address); + if (response.isChanged()) { + r.setRevision(response.getRevision()); + r.setLastRawData(response.getConfigurations()); + data.putAll(response.getConfigurations()); + } + }); + this.configConverter.updateData(data); + break; + } catch (Exception e) { + if (i == 2) { + throw e; + } + LOGGER.warn("firstQueryConfigurations failed, config address {} and ignore {}", address, e.getMessage()); } + i++; } } @@ -90,9 +108,9 @@ private void onDataChanged() { this.configurationsRequests.forEach(r -> latestData.putAll(r.getLastRawData())); Map lastData = configConverter.updateData(latestData); - ConfigurationChangedEvent event = ConfigurationChangedEvent + KieConfigurationChangedEvent event = KieConfigurationChangedEvent .createIncremental(configConverter.getCurrentData(), lastData); - if (event.isChanged()) { + if (!event.getChanged().isEmpty()) { eventBus.post(event); } } @@ -106,6 +124,8 @@ protected void initTaskPool(String taskName) { public void startConfigKieManager() { this.configurationsRequests.forEach((t) -> this.startTask(new PollConfigurationTask(0, t))); + schedulerCheckAddressAvailable("kie-addr-check", new CheckKieAddressTask(), + kieConfiguration.getRefreshIntervalInMillis()); } class PollConfigurationTask implements Task { @@ -121,22 +141,39 @@ public PollConfigurationTask(int failCount, ConfigurationsRequest configurations @Override public void execute() { try { - ConfigurationsResponse response = configKieClient.queryConfigurations(configurationsRequest); + ConfigurationsResponse response = configKieClient.queryConfigurations(configurationsRequest, + kieAddressManager.address()); if (response.isChanged()) { configurationsRequest.setRevision(response.getRevision()); configurationsRequest.setLastRawData(response.getConfigurations()); onDataChanged(); } if (KieConfigManager.this.kieConfiguration.isEnableLongPolling()) { - startTask(new BackOffSleepTask(LONG_POLLING_INTERVAL, new PollConfigurationTask(0, this.configurationsRequest))); + startTask( + new BackOffSleepTask(LONG_POLLING_INTERVAL, new PollConfigurationTask(0, this.configurationsRequest))); } else { - startTask(new BackOffSleepTask(kieConfiguration.getRefreshIntervalInMillis(), new PollConfigurationTask(0, this.configurationsRequest))); + startTask(new BackOffSleepTask(kieConfiguration.getRefreshIntervalInMillis(), + new PollConfigurationTask(0, this.configurationsRequest))); } } catch (Exception e) { - LOGGER.error("get configurations from KieConfigCenter failed, and will try again.", e); + LOGGER.warn("get configurations from KieConfigCenter failed, and will try again, cause message: {}. current " + + "fail does not affect the obtained historical configuration.", e.getCause().getMessage()); startTask( new BackOffSleepTask(failCount + 1, new PollConfigurationTask(failCount + 1, this.configurationsRequest))); } } } + + class CheckKieAddressTask implements Runnable { + @Override + public void run() { + List isolationAddresses = kieAddressManager.getIsolationAddresses(); + if (isolationAddresses.isEmpty()) { + return; + } + for (String address : isolationAddresses) { + configKieClient.checkAddressAvailable(address); + } + } + } } diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigOperation.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigOperation.java index 364774fd22a..135ad0c220a 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigOperation.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigOperation.java @@ -26,8 +26,16 @@ public interface KieConfigOperation { /** * 根据查询条件查询配置项。 * @param request 查询的维度(project, application, serviceName, version) 和 revision 信息。 + * @param address 查询的配置中心地址。 * @return 如果存在配置变更,返回全量的配置项, changed = true。 如果没有变更, 返回 null, changed = false, * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ - ConfigurationsResponse queryConfigurations(ConfigurationsRequest request); + ConfigurationsResponse queryConfigurations(ConfigurationsRequest request, String address); + + /** + * Check kie isolation address available + * + * @param address isolation address + */ + void checkAddressAvailable(String address); } diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigurationChangedEvent.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigurationChangedEvent.java new file mode 100644 index 00000000000..0e22c17e8b9 --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigurationChangedEvent.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config.kie.client; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * This event is fired when configuration changed of kie. + */ +public class KieConfigurationChangedEvent { + private final Map added; + + private final Map deleted; + + private final Map updated; + + private Set changed; + + private KieConfigurationChangedEvent(Map added, Map updated, + Map deleted) { + this.added = added; + this.deleted = deleted; + this.updated = updated; + this.changed = new HashSet<>(); + this.changed.addAll(added.keySet()); + this.changed.addAll(updated.keySet()); + this.changed.addAll(deleted.keySet()); + } + + public static KieConfigurationChangedEvent createIncremental(Map latest, Map last) { + Map itemsCreated = new HashMap<>(); + Map itemsDeleted = new HashMap<>(); + Map itemsModified = new HashMap<>(); + + for (Map.Entry entry : latest.entrySet()) { + String itemKey = entry.getKey(); + if (!last.containsKey(itemKey)) { + itemsCreated.put(itemKey, entry.getValue()); + } else if (!Objects.equals(last.get(itemKey), latest.get(itemKey))) { + itemsModified.put(itemKey, entry.getValue()); + } + } + for (String itemKey : last.keySet()) { + if (!latest.containsKey(itemKey)) { + itemsDeleted.put(itemKey, null); + } + } + KieConfigurationChangedEvent event = KieConfigurationChangedEvent + .createIncremental(itemsCreated, itemsModified, itemsDeleted); + return event; + } + + public static KieConfigurationChangedEvent createIncremental(Map added, Map updated, + Map deleted) { + return new KieConfigurationChangedEvent(added, updated, deleted); + } + + public static KieConfigurationChangedEvent createIncremental(Map updated) { + return new KieConfigurationChangedEvent(new HashMap<>(), updated, new HashMap<>()); + } + + public final Map getAdded() { + return added; + } + + + public final Map getUpdated() { + return updated; + } + + + public final Map getDeleted() { + return deleted; + } + + public final Set getChanged() { + return changed; + } +} diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequest.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequest.java index b0e9344e492..b22f7ecaa23 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequest.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequest.java @@ -80,6 +80,7 @@ public ConfigurationsRequest setLastRawData(Map lastRawData) { @Override public int compareTo(ConfigurationsRequest o) { + // Higher priority, query the last return o.getOrder() - this.order; } } diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequestFactory.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequestFactory.java index 4f2b4325c0f..20c6ba3bacb 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequestFactory.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequestFactory.java @@ -32,13 +32,13 @@ public class ConfigurationsRequestFactory { private static final String KEY_VERSION = "version"; - private static final int VERSION_ORDER = 50; + private static final int CUSTOM_ORDER = 100; - private static final int SERVICE_ORDER = 100; + private static final int VERSION_ORDER = 200; - private static final int APP_ORDER = 200; + private static final int SERVICE_ORDER = 300; - private static final int CUSTOM_ORDER = 300; + private static final int APP_ORDER = 400; public static List buildConfigurationRequests(KieConfiguration configuration) { List result = new ArrayList<>(); diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KVDoc.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KVDoc.java index 4078557d3be..3059a581c61 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KVDoc.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KVDoc.java @@ -44,6 +44,9 @@ public class KVDoc { private String status; + @JsonAlias("update_time") + private long updateTime; + public String getStatus() { return status; } @@ -115,4 +118,12 @@ public void setValue(String value) { public String getValueType() { return valueType; } + + public long getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(long updateTime) { + this.updateTime = updateTime; + } } diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.java index 0742c11fb10..d8c069ee9b1 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.java @@ -27,8 +27,8 @@ public class KieAddressManager extends AbstractAddressManager { - public KieAddressManager(List addresses, EventBus eventBus) { - super(addresses); + public KieAddressManager(List addresses, EventBus eventBus, String region, String availableZone) { + super(addresses, region, availableZone); eventBus.register(this); } diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ValueType.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ValueType.java index b38a1e28f8e..23cbce3b367 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ValueType.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ValueType.java @@ -24,5 +24,6 @@ public enum ValueType { string, text, json, - properties + properties, + xml } diff --git a/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/KieAddressManagerTest.java b/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/KieAddressManagerTest.java index 0e4f5793060..c8bf1f57a29 100644 --- a/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/KieAddressManagerTest.java +++ b/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/KieAddressManagerTest.java @@ -17,6 +17,7 @@ package org.apache.servicecomb.config.kie.client.model; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -36,10 +37,13 @@ class KieAddressManagerTest { private static KieAddressManager addressManager1; @Test - public void kieAddressManagerTest() { + public void kieAddressManagerTest() throws NoSuchFieldException, IllegalAccessException { addresses.add("http://127.0.0.1:30103"); addresses.add("https://127.0.0.2:30103"); - addressManager1 = new KieAddressManager(addresses, new EventBus()); + addressManager1 = new KieAddressManager(addresses, new EventBus(), "", ""); + Field addressManagerField = addressManager1.getClass().getSuperclass().getDeclaredField("index"); + addressManagerField.setAccessible(true); + addressManagerField.set(addressManager1, 0); Assertions.assertNotNull(addressManager1); @@ -51,7 +55,6 @@ public void kieAddressManagerTest() { Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); } - @Test public void onRefreshEndpointEvent() { List addressAZ = new ArrayList<>(); @@ -61,7 +64,7 @@ public void onRefreshEndpointEvent() { Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); - addressManager1 = new KieAddressManager(addresses, new EventBus()); + addressManager1 = new KieAddressManager(addresses, new EventBus(), "", ""); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "KIE"); addressManager1.refreshEndpoint(event, "KIE"); diff --git a/clients/dashboard-client/pom.xml b/clients/dashboard-client/pom.xml index 57bacd97d6a..fb40407b82a 100644 --- a/clients/dashboard-client/pom.xml +++ b/clients/dashboard-client/pom.xml @@ -22,7 +22,7 @@ clients org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardAddressManager.java b/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardAddressManager.java index 9dd8c6a3448..6897e02acd4 100644 --- a/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardAddressManager.java +++ b/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardAddressManager.java @@ -29,8 +29,8 @@ public class DashboardAddressManager extends AbstractAddressManager { - public DashboardAddressManager(List addresses, EventBus eventBus) { - super(addresses); + public DashboardAddressManager(List addresses, EventBus eventBus, String region, String availableZone) { + super(addresses, region, availableZone); eventBus.register(this); } diff --git a/clients/dashboard-client/src/test/java/org/apache/servicecomb/dashboard/client/AddressManagerTest.java b/clients/dashboard-client/src/test/java/org/apache/servicecomb/dashboard/client/AddressManagerTest.java index 0703e0dc6aa..fdb7a23922b 100644 --- a/clients/dashboard-client/src/test/java/org/apache/servicecomb/dashboard/client/AddressManagerTest.java +++ b/clients/dashboard-client/src/test/java/org/apache/servicecomb/dashboard/client/AddressManagerTest.java @@ -17,6 +17,7 @@ package org.apache.servicecomb.dashboard.client; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -35,10 +36,13 @@ class AddressManagerTest { private static DashboardAddressManager addressManager1; @Test - public void kieAddressManagerTest() { + public void kieAddressManagerTest() throws IllegalAccessException, NoSuchFieldException { addresses.add("http://127.0.0.1:30103"); addresses.add("https://127.0.0.2:30103"); - addressManager1 = new DashboardAddressManager(addresses, new EventBus()); + addressManager1 = new DashboardAddressManager(addresses, new EventBus(), "", ""); + Field addressManagerField = addressManager1.getClass().getSuperclass().getDeclaredField("index"); + addressManagerField.setAccessible(true); + addressManagerField.set(addressManager1, 0); Assertions.assertNotNull(addressManager1); @@ -50,7 +54,6 @@ public void kieAddressManagerTest() { Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); } - @Test public void onRefreshEndpointEvent() { List addressAZ = new ArrayList<>(); @@ -60,7 +63,7 @@ public void onRefreshEndpointEvent() { Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); - addressManager1 = new DashboardAddressManager(addresses, new EventBus()); + addressManager1 = new DashboardAddressManager(addresses, new EventBus(), "", ""); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseMonitoring"); addressManager1.refreshEndpoint(event, "CseMonitoring"); diff --git a/clients/http-client-common/pom.xml b/clients/http-client-common/pom.xml index ae58108fb83..51d982e470c 100644 --- a/clients/http-client-common/pom.xml +++ b/clients/http-client-common/pom.xml @@ -22,7 +22,7 @@ clients org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/AbstractAddressManager.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/AbstractAddressManager.java index a126a855fa9..3c5d6214be8 100644 --- a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/AbstractAddressManager.java +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/AbstractAddressManager.java @@ -17,31 +17,23 @@ package org.apache.servicecomb.http.client.common; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.http.client.event.EngineConnectChangedEvent; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; import com.google.common.annotations.VisibleForTesting; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.google.common.eventbus.EventBus; public class AbstractAddressManager { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAddressManager.class); @@ -52,66 +44,118 @@ public class AbstractAddressManager { private static final String V3_PREFIX = "/v3/"; - private static final int DEFAULT_METRICS_WINDOW_TIME = 1; + private static final String ZONE = "availableZone"; + + private static final String REGION = "region"; private static final int ISOLATION_THRESHOLD = 3; - private List addresses = new ArrayList<>(); + private volatile List addresses = new ArrayList<>(); - private int index = 0; + // when all addresses are isolation, it will use this for polling. + private final List defaultAddress = new ArrayList<>(); - private String projectName; + private final List defaultIsolationAddress = new ArrayList<>(); - // if address in same zone will be true; others will be false. - private final Map addressCategory = new HashMap<>(); + private int index; + + private String projectName; // recording continuous times of failure of an address. private final Map addressFailureStatus = new ConcurrentHashMap<>(); - // recording address isolation status, if isolated will be false - private final Map addressIsolated = new ConcurrentHashMap<>(); - - // recording address isolation status, if isolated will be false - private Cache addressIsolationStatus = CacheBuilder.newBuilder() - .maximumSize(100) - .expireAfterWrite(1, TimeUnit.MINUTES) - .build(); - private volatile List availableZone = new ArrayList<>(); + private final List isolationZoneAddress = new ArrayList<>(); + private volatile List availableRegion = new ArrayList<>(); - private final List defaultAddress = new ArrayList<>(); + private final List isolationRegionAddress = new ArrayList<>(); - private boolean addressAutoRefreshed = false; + private volatile boolean addressAutoRefreshed = false; private final Object lock = new Object(); - private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1, - new ThreadFactoryBuilder() - .setNameFormat("check-available-address-%d") - .build()); + private final Random random = new Random(); - public AbstractAddressManager(List addresses) { + private EventBus eventBus; + + public AbstractAddressManager(List addresses, String ownRegion, String ownAvailableZone) { this.projectName = DEFAULT_PROJECT; - this.addresses.addAll(addresses); - this.defaultAddress.addAll(addresses); + parseAndInitAddresses(addresses, ownRegion, ownAvailableZone, false); + this.index = !addresses.isEmpty() ? getRandomIndex() : 0; + } + + /** + * address support config with region/availableZone info, to enable engine affinity calls during startup + * address may be like: + * https://192.168.20.13:30110?region=region1&availableZone=az + * https://192.168.20.13:30100?region=region1&availableZone=az + * When address have no datacenter information, roundRobin using address + * + * @param addresses engine addresses + * @param ownRegion microservice region + * @param ownAvailableZone microservice zone + * @param isFormat is need format + */ + private void parseAndInitAddresses(List addresses, String ownRegion, String ownAvailableZone, + boolean isFormat) { + if (CollectionUtils.isEmpty(addresses)) { + return; + } + List tempList = new ArrayList<>(); + addressAutoRefreshed = addresses.stream().anyMatch(addr -> addr.contains(ZONE) || addr.contains(REGION)); + for (String address : addresses) { + // Compatible IpPortManager init address is 127.0.0.1:30100 + if (!address.startsWith("http")) { + tempList.add(address); + continue; + } + URLEndPoint endpoint = new URLEndPoint(address); + tempList.add(endpoint.toString()); + buildAffinityAddress(endpoint, ownRegion, ownAvailableZone); + } + this.addresses.addAll(isFormat ? this.transformAddress(tempList) : tempList); + this.defaultAddress.addAll(isFormat ? this.transformAddress(tempList) : tempList); } - public AbstractAddressManager(String projectName, List addresses) { + private void buildAffinityAddress(URLEndPoint endpoint, String ownRegion, String ownAvailableZone) { + if (addressAutoRefreshed) { + if (regionAndAZMatch(ownRegion, ownAvailableZone, endpoint.getFirst(REGION), endpoint.getFirst(ZONE))) { + availableZone.add(endpoint.toString()); + } else { + availableRegion.add(endpoint.toString()); + } + } + } + + private boolean regionAndAZMatch(String ownRegion, String ownAvailableZone, String engineRegion, + String engineAvailableZone) { + return ownRegion.equalsIgnoreCase(engineRegion) && ownAvailableZone.equals(engineAvailableZone); + } + + public AbstractAddressManager(String projectName, List addresses, String ownRegion, String ownAvailableZone) { this.projectName = StringUtils.isEmpty(projectName) ? DEFAULT_PROJECT : projectName; - this.addresses = this.transformAddress(addresses); - this.defaultAddress.addAll(this.addresses); + parseAndInitAddresses(addresses, ownRegion, ownAvailableZone, true); + this.index = !addresses.isEmpty() ? getRandomIndex() : 0; } - @VisibleForTesting - Cache getAddressIsolationStatus() { - return addressIsolationStatus; + private int getRandomIndex() { + return random.nextInt(addresses.size()); } - @VisibleForTesting - void setAddressIsolationStatus(Cache addressIsolationStatus) { - this.addressIsolationStatus = addressIsolationStatus; + public void refreshEndpoint(RefreshEndpointEvent event, String key) { + if (null == event || !event.getName().equals(key)) { + return; + } + + availableZone = event.getSameZone().stream().map(this::normalizeUri).collect(Collectors.toList()); + availableRegion = event.getSameRegion().stream().map(this::normalizeUri).collect(Collectors.toList()); + addressAutoRefreshed = true; + } + + protected String normalizeUri(String endpoint) { + return new URLEndPoint(endpoint).toString(); } @VisibleForTesting @@ -131,25 +175,10 @@ public List getAvailableRegion() { return availableRegion; } - private void startCheck() { - executorService.scheduleAtFixedRate(this::checkHistory, - 0, - DEFAULT_METRICS_WINDOW_TIME, - TimeUnit.MINUTES); - } - public String formatUrl(String url, boolean absoluteUrl, String address) { return absoluteUrl ? address + url : formatAddress(address) + url; } - public String address() { - if (!addressAutoRefreshed) { - return getDefaultAddress(); - } else { - return getAvailableZoneAddress(); - } - } - public boolean sslEnabled() { return address().startsWith("https://"); } @@ -158,10 +187,6 @@ protected List transformAddress(List addresses) { return addresses.stream().map(this::formatAddress).collect(Collectors.toList()); } - protected String getUrlPrefix(String address) { - return address + V3_PREFIX; - } - protected String formatAddress(String address) { try { return getUrlPrefix(address) + HttpUtils.encodeURLParam(this.projectName); @@ -170,27 +195,34 @@ protected String formatAddress(String address) { } } - private String getDefaultAddress() { - List addresses = getAvailableAddress(defaultAddress); - if (!addresses.isEmpty()) { - return getCurrentAddress(addresses); + protected String getUrlPrefix(String address) { + return address + V3_PREFIX; + } + + public String address() { + if (!addressAutoRefreshed) { + return getDefaultAddress(); + } else { + return getAvailableZoneAddress(); } - return getInitAddress(); } - private String getAvailableZoneAddress() { - List addresses = getAvailableZoneIpPorts(); + private String getDefaultAddress() { if (!addresses.isEmpty()) { return getCurrentAddress(addresses); } - return getInitAddress(); + LOGGER.warn("all addresses are isolation, please check server status."); + // when all addresses are isolation, it will use all default address for polling. + return getCurrentAddress(defaultAddress); } - // when all available address is fail, it will use all the initial addresses for polling. - private String getInitAddress() { - if (addresses.isEmpty()) { - return null; + private String getAvailableZoneAddress() { + List zoneOrRegionAddress = getZoneOrRegionAddress(); + if (!zoneOrRegionAddress.isEmpty()) { + return getCurrentAddress(zoneOrRegionAddress); } + LOGGER.warn("all auto discovery addresses are isolation, please check server status."); + // when all available address are isolation, it will use config addresses for polling. return getCurrentAddress(addresses); } @@ -204,36 +236,41 @@ private String getCurrentAddress(List addresses) { } } - private List getAvailableZoneIpPorts() { + private List getZoneOrRegionAddress() { List results = new ArrayList<>(); if (!availableZone.isEmpty()) { - results.addAll(getAvailableAddress(availableZone)); + results.addAll(availableZone); } else { - results.addAll(getAvailableAddress(availableRegion)); + results.addAll(availableRegion); } return results; } - private List getAvailableAddress(List endpoints) { - return endpoints.stream().filter(uri -> !addressIsolated.containsKey(uri) || addressIsolated.get(uri)) - .collect(Collectors.toList()); - } - - protected String normalizeUri(String endpoint) { - return new URLEndPoint(endpoint).toString(); - } - - public void refreshEndpoint(RefreshEndpointEvent event, String key) { - if (null == event || !event.getName().equals(key)) { + public void recordSuccessState(String address) { + resetFailureStatus(address); + if (addressAutoRefreshed) { + if (isolationZoneAddress.remove(address)) { + LOGGER.warn("restore same region address [{}]", address); + if (eventBus != null && availableZone.isEmpty()) { + eventBus.post(new EngineConnectChangedEvent()); + } + availableZone.add(address); + return; + } + if (isolationRegionAddress.remove(address)) { + LOGGER.warn("restore same zone address [{}]", address); + availableRegion.add(address); + } return; } + if (defaultIsolationAddress.remove(address)) { + LOGGER.warn("restore default address [{}]", address); + addresses.add(address); + } + } - availableZone = event.getSameZone().stream().map(this::normalizeUri).collect(Collectors.toList()); - availableRegion = event.getSameRegion().stream().map(this::normalizeUri).collect(Collectors.toList()); - availableZone.forEach(address -> addressCategory.put(address, true)); - availableRegion.forEach(address -> addressCategory.put(address, false)); - startCheck(); - addressAutoRefreshed = true; + public void resetFailureStatus(String address) { + addressFailureStatus.put(address, 0); } public void recordFailState(String address) { @@ -251,96 +288,61 @@ public void recordFailState(String address) { } } - public void recordSuccessState(String address) { - addressFailureStatus.put(address, 0); - } - + //Query whether the current address belongs to the same AZ or the same region through AZMap, + // and delete it from the record. At the same time, add records in history and cache @VisibleForTesting - protected void checkHistory() { - addressIsolated.keySet().stream().filter(this::judgeIsolation).forEach(s -> { - if (telnetTest(s)) { - rejoinAddress(s); - } else { - addressIsolationStatus.put(s, false); + void removeAddress(String address) { + if (!addressAutoRefreshed) { + if (addresses.remove(address)) { + LOGGER.warn("isolation default address [{}]", address); + defaultIsolationAddress.add(address); } - }); - } - - private Boolean judgeIsolation(String address) { - try { - return addressIsolationStatus.get(address, () -> true); - } catch (ExecutionException e) { - return true; + return; } - } - - protected boolean telnetTest(String address) { - URI ipPort = parseIpPortFromURI(address); - try (Socket s = new Socket()) { - s.connect(new InetSocketAddress(ipPort.getHost(), ipPort.getPort()), 3000); - return true; - } catch (IOException e) { - LOGGER.warn("ping endpoint {} failed, It will be quarantined again.", address); + if (availableZone.remove(address)) { + LOGGER.warn("isolation same zone address [{}]", address); + isolationZoneAddress.add(address); + if (eventBus != null && availableZone.isEmpty() && !availableRegion.isEmpty()) { + eventBus.post(new EngineConnectChangedEvent()); + } + return; } - return false; - } - - private URI parseIpPortFromURI(String uri) { - try { - return new URI(uri); - } catch (URISyntaxException e) { - return null; + if (availableRegion.remove(address)) { + LOGGER.warn("isolation same region address [{}]", address); + isolationRegionAddress.add(address); } } - //Query whether the current address belongs to the same AZ or region through azmap, - // add it to the sequence of, and delete the record in history - @VisibleForTesting - void rejoinAddress(String address) { - if (!addressAutoRefreshed) { - defaultAddress.add(address); - addressFailureStatus.put(address, 0); - addressIsolated.remove(address); - return; - } - - if (addressCategory.get(address) == null) { - LOGGER.warn("may not happen {}-{}", addressCategory.size(), address); - return; - } + public void setEventBus(EventBus eventBus) { + this.eventBus = eventBus; + } - if (addressCategory.get(address)) { - availableZone.add(address); - } else { - availableRegion.add(address); - } - addressFailureStatus.put(address, 0); - addressIsolated.remove(address); + public List getIsolationAddresses() { + List isolationAddresses = new ArrayList<>(defaultIsolationAddress); + isolationAddresses.addAll(isolationZoneAddress); + isolationAddresses.addAll(isolationRegionAddress); + return isolationAddresses; } - //Query whether the current address belongs to the same AZ or the same region through AZMap, - // and delete it from the record. At the same time, add records in history and cache - @VisibleForTesting - void removeAddress(String address) { - if (!addressAutoRefreshed) { - defaultAddress.remove(address); - addressIsolated.put(address, false); - addressIsolationStatus.put(address, false); - return; + public String compareAndGetAddress(String host) { + for (String address : defaultAddress) { + if (isAddressHostSame(address, host)) { + return address; + } } + return ""; + } - if (addressCategory.get(address) == null) { - LOGGER.warn("may not happen {}-{}", addressCategory.size(), address); - return; + private boolean isAddressHostSame(String address, String host) { + if (StringUtils.isEmpty(host)) { + return false; } - - if (addressCategory.get(address)) { - availableZone.remove(address); - } else { - availableRegion.remove(address); + try { + URI uri = new URI(address); + return host.equals(uri.getHost()); + } catch (Exception e) { + LOGGER.warn("Exception occurred while constructing URI using the address [{}]", address); } - - addressIsolated.put(address, false); - addressIsolationStatus.put(address, false); + return false; } } diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportImpl.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportImpl.java index da87892929c..507d8d1c556 100644 --- a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportImpl.java +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportImpl.java @@ -18,17 +18,21 @@ package org.apache.servicecomb.http.client.common; import java.io.IOException; +import java.net.URI; import java.util.Map; import org.apache.http.client.HttpClient; import org.apache.http.util.EntityUtils; import org.apache.servicecomb.foundation.auth.SignRequest; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Created by on 2019/10/16. */ public class HttpTransportImpl implements HttpTransport { + private static final Logger LOGGER = LoggerFactory.getLogger(HttpTransportImpl.class); private static final String HEADER_CONTENT_TYPE = "Content-Type"; @@ -87,7 +91,7 @@ public HttpResponse doRequest(HttpRequest httpRequest) throws IOException { globalHeaders.forEach(httpRequest::addHeader); } - httpRequest.getHeaders().putAll(requestAuthHeaderProvider.loadAuthHeader(createSignRequest())); + httpRequest.getHeaders().putAll(requestAuthHeaderProvider.loadAuthHeader(createSignRequest(httpRequest.getUrl()))); //get Http response org.apache.http.HttpResponse response = httpClient.execute(httpRequest.getRealRequest()); @@ -98,9 +102,16 @@ public HttpResponse doRequest(HttpRequest httpRequest) throws IOException { response.getAllHeaders()); } - private static SignRequest createSignRequest() { - // Now the implementations do not process SignRequest, so return null. Maybe future will use it. - return null; + private static SignRequest createSignRequest(String url) { + try { + URI uri = URI.create(url); + SignRequest signRequest = new SignRequest(); + signRequest.setEndpoint(uri); + return signRequest; + } catch (Exception e) { + LOGGER.error("create signRequest failed!", e); + return null; + } } @Override diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpUtils.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpUtils.java index 37d5c207473..5f259cc3053 100644 --- a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpUtils.java +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpUtils.java @@ -18,6 +18,7 @@ package org.apache.servicecomb.http.client.common; import java.io.IOException; +import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -55,7 +56,14 @@ public static String encodeURLParam(String value) throws IOException { if (value == null) { return ""; } - return URLEncoder.encode(value, "UTF-8"); + return URLEncoder.encode(value, StandardCharsets.UTF_8); + } + + public static String decodeURLParam(String value) throws IOException { + if (value == null) { + return null; + } + return URLDecoder.decode(value, StandardCharsets.UTF_8); } public static String sha256Encode(String key, String data) throws Exception { diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/URLEndPoint.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/URLEndPoint.java index ba295c35363..e3e53bad06a 100644 --- a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/URLEndPoint.java +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/URLEndPoint.java @@ -36,7 +36,7 @@ public class URLEndPoint { private final boolean sslEnabled; - private final Map> querys; + private final Map> queries; private final String hostOrIp; @@ -49,7 +49,7 @@ public URLEndPoint(String endpoint) { throw new IllegalArgumentException("port not specified."); } port = uri.getPort(); - querys = splitQuery(uri); + queries = splitQuery(uri); if (endpoint.contains(HTTPS_KEY)) { sslEnabled = true; } else { @@ -68,7 +68,7 @@ public static Map> splitQuery(URI uri) { } public String getFirst(String key) { - List values = querys.get(key); + List values = queries.get(key); if (values == null) { return null; } diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/EngineConnectChangedEvent.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/EngineConnectChangedEvent.java new file mode 100644 index 00000000000..b2029f5c38c --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/EngineConnectChangedEvent.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.http.client.event; + +public class EngineConnectChangedEvent { +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/OperationEvents.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/OperationEvents.java similarity index 79% rename from clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/OperationEvents.java rename to clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/OperationEvents.java index 62274519ea4..69896f750ec 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/OperationEvents.java +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/OperationEvents.java @@ -15,10 +15,18 @@ * limitations under the License. */ -package org.apache.servicecomb.service.center.client; +package org.apache.servicecomb.http.client.event; public abstract class OperationEvents { public static class UnAuthorizedOperationEvent extends OperationEvents { + private final String address; + public UnAuthorizedOperationEvent(String address) { + this.address = address; + } + + public String getAddress() { + return address; + } } } diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/task/AbstractTask.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/task/AbstractTask.java index deba2d8521d..cc10ae8e142 100644 --- a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/task/AbstractTask.java +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/task/AbstractTask.java @@ -20,7 +20,9 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +31,7 @@ public class AbstractTask { public class BackOffSleepTask implements Task { private static final long BASE = 3000; - private static final long MAX = 10 * 60 * 10000; + private static final long MAX = 10 * 60 * 1000; long waitTime; @@ -63,6 +65,10 @@ public void execute() { private volatile boolean running = true; + public static AtomicInteger taskCounter = new AtomicInteger(0); + + private ScheduledExecutorService addrCheckExecutor; + protected AbstractTask(String taskName) { initTaskPool(taskName); Runtime.getRuntime().addShutdownHook(new Thread(AbstractTask.this::stop, taskName + "-shutdown-hook")); @@ -70,7 +76,14 @@ protected AbstractTask(String taskName) { protected void initTaskPool(String taskName) { this.taskPool = Executors.newSingleThreadExecutor((task) -> - new Thread(task, taskName)); + new Thread(task, taskName + "-" + taskCounter.getAndIncrement())); + } + + protected void schedulerCheckAddressAvailable(String taskName, Runnable task, long delayTime) { + if (addrCheckExecutor == null) { + addrCheckExecutor = Executors.newScheduledThreadPool(1, (t) -> new Thread(t, taskName)); + } + addrCheckExecutor.scheduleWithFixedDelay(task, delayTime, delayTime, TimeUnit.MILLISECONDS); } protected void startTask(Task task) { @@ -96,6 +109,10 @@ public void stop() { running = false; this.taskPool.shutdown(); this.taskPool.awaitTermination(10, TimeUnit.SECONDS); + if (addrCheckExecutor != null) { + this.addrCheckExecutor.shutdown(); + this.addrCheckExecutor.awaitTermination(10, TimeUnit.SECONDS); + } } catch (InterruptedException e) { LOGGER.warn("tasks not shutdown in time {}", e.getMessage()); } diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/utils/ServiceCombServiceAvailableUtils.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/utils/ServiceCombServiceAvailableUtils.java new file mode 100644 index 00000000000..9139e65f96a --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/utils/ServiceCombServiceAvailableUtils.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.servicecomb.http.client.utils; + +import org.apache.http.HttpStatus; +import org.apache.servicecomb.http.client.common.AbstractAddressManager; +import org.apache.servicecomb.http.client.common.HttpRequest; +import org.apache.servicecomb.http.client.common.HttpResponse; +import org.apache.servicecomb.http.client.common.HttpTransport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.URI; +import java.net.URISyntaxException; + +public class ServiceCombServiceAvailableUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCombServiceAvailableUtils.class); + + public static void checkAddressAvailable(AbstractAddressManager addressManager, String address, + HttpTransport httpTransport, String path) { + String formatUrl = addressManager.formatUrl(path, true, address); + HttpRequest httpRequest = new HttpRequest(formatUrl, null, null, HttpRequest.GET); + try { + HttpResponse response = httpTransport.doRequest(httpRequest); + if (response.getStatusCode() == HttpStatus.SC_OK) { + addressManager.recordSuccessState(address); + return; + } + + // old server does not provide the check api, using TCP checks whether the server is ready. + if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND && telnetCheckAddress(address)) { + LOGGER.warn("[{}] path does not provide, tcp check address ready!", path); + addressManager.recordSuccessState(address); + } + } catch (IOException e) { + LOGGER.error("check isolation address [{}] available error!", address); + } + } + + private static boolean telnetCheckAddress(String address) { + URI ipPort = parseIpPortFromURI(address); + if (ipPort == null) { + return false; + } + try (Socket s = new Socket()) { + s.connect(new InetSocketAddress(ipPort.getHost(), ipPort.getPort()), 3000); + return true; + } catch (IOException e) { + LOGGER.warn("ping endpoint {} failed, It will be quarantined again.", address); + } + return false; + } + + private static URI parseIpPortFromURI(String address) { + try { + return new URI(address); + } catch (URISyntaxException e) { + LOGGER.error("build uri error with address [{}].", address); + return null; + } + } +} diff --git a/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/AbstractAddressManagerTest.java b/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/AbstractAddressManagerTest.java index 080e86f1657..b14d6827cb8 100644 --- a/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/AbstractAddressManagerTest.java +++ b/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/AbstractAddressManagerTest.java @@ -17,6 +17,7 @@ package org.apache.servicecomb.http.client.common; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -31,9 +32,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; - public class AbstractAddressManagerTest { private static final List addresses = new ArrayList<>(); @@ -45,12 +43,21 @@ public class AbstractAddressManagerTest { private static AbstractAddressManager addressManager3; @BeforeEach - public void setUp() { + public void setUp() throws NoSuchFieldException, IllegalAccessException { addresses.add("http://127.0.0.1:30103"); addresses.add("https://127.0.0.2:30103"); - addressManager1 = new AbstractAddressManager(addresses); - addressManager2 = new AbstractAddressManager("project", addresses); - addressManager3 = new AbstractAddressManager(null, addresses); + addressManager1 = new AbstractAddressManager(addresses, "", ""); + addressManager2 = new AbstractAddressManager("project", addresses, "", ""); + addressManager3 = new AbstractAddressManager(null, addresses, "", ""); + Field addressManagerField = addressManager1.getClass().getDeclaredField("index"); + addressManagerField.setAccessible(true); + addressManagerField.set(addressManager1, 0); + addressManagerField = addressManager2.getClass().getDeclaredField("index"); + addressManagerField.setAccessible(true); + addressManagerField.set(addressManager2, 0); + addressManagerField = addressManager3.getClass().getDeclaredField("index"); + addressManagerField.setAccessible(true); + addressManagerField.set(addressManager3, 0); } @AfterEach @@ -81,12 +88,7 @@ public void recordStateTest() throws ExecutionException { zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST"); - AbstractAddressManager addressManager = new AbstractAddressManager(addresses) { - @Override - protected boolean telnetTest(String address) { - return true; - } - }; + AbstractAddressManager addressManager = new AbstractAddressManager(addresses, "", "") {}; addressManager.refreshEndpoint(event, "TEST"); @@ -99,7 +101,7 @@ protected boolean telnetTest(String address) { Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); // test fail 2 times ,it will not be isolated - addressManager.recordSuccessState(address); + addressManager.resetFailureStatus(address); Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); // test recodeStatus times @@ -112,20 +114,8 @@ protected boolean telnetTest(String address) { addressManager.recordFailState(address); Assertions.assertEquals("http://127.0.0.4:30100", addressManager.address()); - // mock cacheAddress status refresh after 10 minute - Cache cache = CacheBuilder.newBuilder() - .maximumSize(100) - .expireAfterWrite(10, TimeUnit.MINUTES) - .build(); - cache.put("http://127.0.0.3:30100", true); - - addressManager.setAddressIsolationStatus(cache); - Cache result = addressManager.getAddressIsolationStatus(); - Assertions.assertEquals(true, result.get("http://127.0.0.3:30100", () -> false)); - // test restore isolation - addressManager.checkHistory(); - addressManager.rejoinAddress("http://127.0.0.3:30100"); + addressManager.recordSuccessState("http://127.0.0.3:30100"); Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); } @@ -134,7 +124,7 @@ protected boolean telnetTest(String address) { @Test public void testMultipleThread() throws Exception { - AbstractAddressManager addressManager = new AbstractAddressManager(addresses); + AbstractAddressManager addressManager = new AbstractAddressManager(addresses, "", ""); String address = "http://127.0.0.3:30100"; CountDownLatch latch = new CountDownLatch(2); @@ -312,4 +302,27 @@ public void normalizeIPV6Test() { uri = addressManager1.normalizeUri("rest://[2008::7:957f:b2d6:1af4:a1f8]:30100"); Assertions.assertEquals("http://[2008::7:957f:b2d6:1af4:a1f8]:30100", uri); } + + @Test + public void compareAndGetAddressTest() { + List testAddr = new ArrayList<>(); + testAddr.add("https://192.168.20.160:30100"); + testAddr.add("https://127.0.0.1:30100"); + testAddr.add("https://127.0.0.3:30100"); + AbstractAddressManager manager = new AbstractAddressManager(testAddr, "", ""); + Assertions.assertTrue(manager.compareAndGetAddress("192.168.20.16").isEmpty()); + Assertions.assertEquals("https://192.168.20.160:30100", manager.compareAndGetAddress("192.168.20.160")); + } + + @Test + public void AddressAffinityTest() { + List testAddr = new ArrayList<>(); + testAddr.add("https://192.168.20.160:30100?region=region1&availableZone=zone1"); + testAddr.add("https://127.0.0.1:30100"); + AbstractAddressManager manager = new AbstractAddressManager(testAddr, "region1", "zone1"); + Assertions.assertEquals("https://192.168.20.160:30100", manager.address()); + + AbstractAddressManager manager2 = new AbstractAddressManager("default", testAddr, "region1", "zone1"); + Assertions.assertEquals("https://192.168.20.160:30100", manager2.address()); + } } diff --git a/clients/pom.xml b/clients/pom.xml index df660f6c051..49f481934cc 100644 --- a/clients/pom.xml +++ b/clients/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default 4.0.0 diff --git a/clients/service-center-client/README.md b/clients/service-center-client/README.md index b3a933bf9a3..a6b4c6d4936 100644 --- a/clients/service-center-client/README.md +++ b/clients/service-center-client/README.md @@ -1,15 +1,15 @@ ## ServiceComb-Service-Center Client for Java - Sample Java client for ServiceComb-Service-Center HTTP API. + Sample Java client for ServiceComb-Service-Center HTTP API. If you want more information about the ServiceComb-Service-Center HTTP API, go [here](https://github.com/apache/servicecomb-service-center/blob/master/docs/openapi/v4.yaml). - + ### Build & Install - + local Build from source : ``` maven clean install ``` - + add dependency to maven ``` @@ -79,14 +79,14 @@ MicroservicesResponse services = client.getMicroserviceList(); for(Microservice microservice : services.getServices()) { for (MicroserviceInstance instance: client.getMicroserviceInstanceList(microservice.getServiceId()).getInstances()) - { - client.sendHeartBeats(new HeartbeatsRequest(microservice.getServiceId(),instance.getInstanceId())); + { + client.sendHeartBeats(new HeartbeatsRequest(microservice.getServiceId(),instance.getInstanceId())); } } ``` #### Other API -You can see client API code and tests, go [here](https://github.com/apache/servicecomb-java-chassis/blob/master/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java) +You can see client API code and tests, go [here](https://github.com/apache/servicecomb-java-chassis/blob/master/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java) ### More development diff --git a/clients/service-center-client/pom.xml b/clients/service-center-client/pom.xml index 4eb051fda9f..ccc3900e9db 100755 --- a/clients/service-center-client/pom.xml +++ b/clients/service-center-client/pom.xml @@ -22,7 +22,7 @@ clients org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManager.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManager.java index a5fd128ab1e..b19db919e81 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManager.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManager.java @@ -26,8 +26,9 @@ import com.google.common.eventbus.Subscribe; public class ServiceCenterAddressManager extends AbstractAddressManager { - public ServiceCenterAddressManager(String projectName, List addresses, EventBus eventBus) { - super(projectName, addresses); + public ServiceCenterAddressManager(String projectName, List addresses, EventBus eventBus, String region, + String availableZone) { + super(projectName, addresses, region, availableZone); eventBus.register(this); } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java index 09f30dc93da..97aeaa9931a 100755 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java @@ -25,6 +25,7 @@ import java.util.Map; import org.apache.http.HttpStatus; +import org.apache.http.client.config.RequestConfig; import org.apache.http.client.utils.URIBuilder; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; @@ -32,13 +33,13 @@ import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpTransportFactory; import org.apache.servicecomb.http.client.common.HttpUtils; -import org.apache.servicecomb.service.center.client.OperationEvents.UnAuthorizedOperationEvent; import org.apache.servicecomb.service.center.client.exception.OperationException; import org.apache.servicecomb.service.center.client.model.CreateMicroserviceInstanceRequest; import org.apache.servicecomb.service.center.client.model.CreateMicroserviceRequest; import org.apache.servicecomb.service.center.client.model.CreateSchemaRequest; import org.apache.servicecomb.service.center.client.model.ErrorMessage; import org.apache.servicecomb.service.center.client.model.FindMicroserviceInstancesResponse; +import org.apache.servicecomb.service.center.client.model.Framework; import org.apache.servicecomb.service.center.client.model.GetSchemaListResponse; import org.apache.servicecomb.service.center.client.model.GetSchemaResponse; import org.apache.servicecomb.service.center.client.model.HeartbeatsRequest; @@ -58,6 +59,7 @@ import org.apache.servicecomb.service.center.client.model.UpdatePropertiesRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; @@ -65,16 +67,24 @@ public class ServiceCenterClient implements ServiceCenterOperation { private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterClient.class); + private static final String CLIENT_CONNECT_TIMEOUT = "servicecomb.registry.sc.client.timeout.connect"; + + private static final String CLIENT_REQUEST_TIMEOUT = "servicecomb.registry.sc.client.timeout.request"; + + private static final String CLIENT_SOCKET_TIMEOUT = "servicecomb.registry.sc.client.timeout.socket"; + private final ServiceCenterRawClient httpClient; - private EventBus eventBus; + private final ServiceCenterAddressManager addressManager; - public ServiceCenterClient(ServiceCenterRawClient httpClient) { + public ServiceCenterClient(ServiceCenterRawClient httpClient, ServiceCenterAddressManager addressManager) { this.httpClient = httpClient; + this.addressManager = addressManager; } public ServiceCenterClient setEventBus(EventBus eventBus) { - this.eventBus = eventBus; + addressManager.setEventBus(eventBus); + this.httpClient.setEventBus(eventBus); return this; } @@ -82,14 +92,28 @@ public ServiceCenterClient(ServiceCenterAddressManager addressManager, SSLProperties sslProperties, RequestAuthHeaderProvider requestAuthHeaderProvider, String tenantName, - Map extraGlobalHeaders) { - HttpTransport httpTransport = HttpTransportFactory.createHttpTransport(sslProperties, requestAuthHeaderProvider); + Map extraGlobalHeaders, + Environment environment) { + HttpTransport httpTransport = HttpTransportFactory.createHttpTransport(sslProperties, requestAuthHeaderProvider, + buildRequestConfig(environment)); httpTransport.addHeaders(extraGlobalHeaders); this.httpClient = new ServiceCenterRawClient.Builder() .setTenantName(tenantName) .setAddressManager(addressManager) .setHttpTransport(httpTransport).build(); + this.addressManager = addressManager; + } + + private RequestConfig buildRequestConfig(Environment environment) { + RequestConfig.Builder builder = HttpTransportFactory.defaultRequestConfig(); + if (environment == null) { + return builder.build(); + } + builder.setConnectTimeout(environment.getProperty(CLIENT_CONNECT_TIMEOUT, int.class, 5000)); + builder.setConnectionRequestTimeout(environment.getProperty(CLIENT_REQUEST_TIMEOUT, int.class, 5000)); + builder.setSocketTimeout(environment.getProperty(CLIENT_SOCKET_TIMEOUT, int.class, 5000)); + return builder.build(); } @Override @@ -99,7 +123,6 @@ public MicroserviceInstancesResponse getServiceCenterInstances() { if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), MicroserviceInstancesResponse.class); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service-center instances fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -120,7 +143,6 @@ public RegisteredMicroserviceResponse registerMicroservice(Microservice microser if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), RegisteredMicroserviceResponse.class); } - sendUnAuthorizedEvent(response); throw new OperationException( "register service fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -138,7 +160,6 @@ public MicroservicesResponse getMicroserviceList() { if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), MicroservicesResponse.class); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service List fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -163,7 +184,6 @@ public RegisteredMicroserviceResponse queryServiceId(Microservice microservice) if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), RegisteredMicroserviceResponse.class); } - sendUnAuthorizedEvent(response); LOGGER.info("Query serviceId fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); @@ -186,7 +206,6 @@ public Microservice getMicroserviceByServiceId(String serviceId) { .deserialize(response.getContent(), MicroserviceResponse.class); return microserviceResponse.getService(); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service message fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -208,7 +227,6 @@ public RegisteredMicroserviceInstanceResponse registerMicroserviceInstance(Micro if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), RegisteredMicroserviceInstanceResponse.class); } - sendUnAuthorizedEvent(response); throw new OperationException( "register service instance fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -244,7 +262,11 @@ public FindMicroserviceInstancesResponse findMicroserviceInstance(String consume result.setModified(false); return result; } - sendUnAuthorizedEvent(response); + if (response.getStatusCode() == HttpStatus.SC_TOO_MANY_REQUESTS) { + LOGGER.warn("rate limited, keep the local service {}#{} instance cache unchanged.", appId, serviceName); + result.setModified(false); + return result; + } throw new OperationException( "get service instances list fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -263,7 +285,6 @@ public MicroserviceInstancesResponse getMicroserviceInstanceList(String serviceI if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), MicroserviceInstancesResponse.class); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service instances list fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -284,7 +305,6 @@ public MicroserviceInstance getMicroserviceInstance(String serviceId, String ins .deserialize(response.getContent(), MicroserviceInstanceResponse.class); return instanceResponse.getInstance(); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service instance message fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -304,7 +324,6 @@ public void deleteMicroserviceInstance(String serviceId, String instanceId) { LOGGER.info("Delete service instance successfully."); return; } - sendUnAuthorizedEvent(response); throw new OperationException( "delete service instance fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -324,7 +343,6 @@ public boolean updateMicroserviceInstanceStatus(String serviceId, String instanc if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } - sendUnAuthorizedEvent(response); throw new OperationException( "update service instance status fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -343,7 +361,6 @@ public void sendHeartBeats(HeartbeatsRequest heartbeatsRequest) { if (response.getStatusCode() == HttpStatus.SC_OK) { return; } - sendUnAuthorizedEvent(response); throw new OperationException( "heartbeats fails, statusCode = " + response.getStatusCode() + "; message = " + response.getMessage() + "; content = " + response.getContent()); @@ -363,7 +380,6 @@ public boolean sendHeartBeat(String serviceId, String instanceId) { if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } - sendUnAuthorizedEvent(response); throw new OperationException( "heartbeats fails, statusCode = " + response.getStatusCode() + "; message = " + response.getMessage() + "; content = " + response.getContent()); @@ -388,7 +404,6 @@ public List getServiceSchemasList(String serviceId, boolean withCont .deserialize(response.getContent(), GetSchemaListResponse.class); return getSchemaResponse.getSchemas(); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service schemas list fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -415,7 +430,6 @@ public String getServiceSchemaContext(String serviceId, String schemaId) { GetSchemaResponse getSchemaResponse = HttpUtils.deserialize(response.getContent(), GetSchemaResponse.class); return getSchemaResponse.getSchema(); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service schema context fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -435,7 +449,6 @@ public boolean registerSchema(String serviceId, String schemaId, CreateSchemaReq if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } - sendUnAuthorizedEvent(response); throw new OperationException( "update service schema fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -458,7 +471,6 @@ public boolean updateServiceSchemaContext(String serviceId, SchemaInfo schemaInf if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } - sendUnAuthorizedEvent(response); throw new OperationException( "update service schema fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -478,7 +490,6 @@ public boolean batchUpdateServiceSchemaContext(String serviceId, ModifySchemasRe if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } - sendUnAuthorizedEvent(response); throw new OperationException( "update service schema fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -489,18 +500,12 @@ public boolean batchUpdateServiceSchemaContext(String serviceId, ModifySchemasRe } } - private void sendUnAuthorizedEvent(HttpResponse response) { - if (this.eventBus != null && response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { - this.eventBus.post(new UnAuthorizedOperationEvent()); - } - } - @Override - public RbacTokenResponse queryToken(RbacTokenRequest request) { + public RbacTokenResponse queryToken(RbacTokenRequest request, String host) { try { + String queryAddress = addressManager.compareAndGetAddress(host); HttpResponse response = httpClient - .postHttpRequestAbsoluteUrl("/v4/token", null, - HttpUtils.serialize(request)); + .postHttpRequestAbsoluteUrl("/v4/token", null, HttpUtils.serialize(request), queryAddress); if (response.getStatusCode() == HttpStatus.SC_OK) { RbacTokenResponse result = HttpUtils.deserialize(response.getContent(), RbacTokenResponse.class); result.setStatusCode(HttpStatus.SC_OK); @@ -530,23 +535,35 @@ public RbacTokenResponse queryToken(RbacTokenRequest request) { } @Override - public boolean updateMicroserviceProperties(String serviceId, Map serviceProperties) { + public boolean updateMicroserviceProperties(String serviceId, Map serviceProperties, + Framework framework) { try { UpdatePropertiesRequest request = new UpdatePropertiesRequest(); request.setProperties(serviceProperties); + request.setFramework(framework); HttpResponse response = httpClient.putHttpRequest( "/registry/microservices/" + serviceId + "/properties", null, HttpUtils.serialize(request)); if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } - sendUnAuthorizedEvent(response); throw new OperationException( - "update service instance status fails, statusCode = " + response.getStatusCode() + "; message = " + response + "update service properties fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( - "update service instance status fails", e); + "update service properties fails", e); + } + } + + @Override + public void checkIsolationAddressAvailable() { + List isolationAddresses = addressManager.getIsolationAddresses(); + if (isolationAddresses.isEmpty()) { + return; + } + for (String address : isolationAddresses) { + httpClient.checkAddressAvailable(address); } } } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterDiscovery.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterDiscovery.java index ccc8fb1697a..a1ab524c187 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterDiscovery.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterDiscovery.java @@ -17,12 +17,12 @@ package org.apache.servicecomb.service.center.client; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import org.apache.servicecomb.http.client.task.AbstractTask; @@ -101,6 +101,8 @@ public static class SubscriptionValue { private final Object lock = new Object(); + private final Random random = new Random(); + public ServiceCenterDiscovery(ServiceCenterClient serviceCenterClient, EventBus eventBus) { super("service-center-discovery-task"); this.serviceCenterClient = serviceCenterClient; @@ -153,13 +155,12 @@ public void onPullInstanceEvent(PullInstanceEvent event) { startTask(new PullInstanceOnceTask()); } - private List pullInstance(SubscriptionKey k, SubscriptionValue v, boolean sendChangedEvent) { + private void pullInstance(SubscriptionKey k, SubscriptionValue v, boolean sendChangedEvent) { if (myselfServiceId == null) { // registration not ready - return Collections.emptyList(); + return; } - List failedKeys = new ArrayList<>(); try { FindMicroserviceInstancesResponse instancesResponse = serviceCenterClient .findMicroserviceInstance(myselfServiceId, k.appId, k.serviceName, ALL_VERSION, v.revision); @@ -167,10 +168,10 @@ private List pullInstance(SubscriptionKey k, SubscriptionValue List instances = instancesResponse.getMicroserviceInstancesResponse().getInstances() == null ? Collections.emptyList() : instancesResponse.getMicroserviceInstancesResponse().getInstances(); setMicroserviceInfo(instances); - LOGGER.info("Instance changed event, " - + "current: revision={}, instances={}; " - + "origin: revision={}, instances={}; " - + "appId={}, serviceName={}", + LOGGER.info("Instance changed event. " + + "Current: revision={}, instances={}. " + + "Origin: revision={}, instances={}. " + + "appId={}, serviceName={}.", instancesResponse.getRevision(), instanceToString(instances), v.revision, @@ -186,14 +187,9 @@ private List pullInstance(SubscriptionKey k, SubscriptionValue } } } catch (Exception e) { - LOGGER.error("find service {}#{} instance failed.", k.appId, k.serviceName, e); - if (!(e.getCause() instanceof IOException)) { - // for IOException, do not remove cache, or when service center - // not available, invocation between microservices will fail. - failedKeys.add(k); - } + LOGGER.warn("find service {}#{} instance failed, remaining local instances cache [{}], cause message: {}", + k.appId, k.serviceName, instanceToString(v.instancesCache), e.getMessage()); } - return failedKeys; } private void setMicroserviceInfo(List instances) { @@ -216,10 +212,17 @@ class PullInstanceTask implements Task { public void execute() { pullAllInstance(); - startTask(new BackOffSleepTask(pollInterval, new PullInstanceTask())); + startTask(new BackOffSleepTask(buildPollIntervalWithSalt(), new PullInstanceTask())); } } + private long buildPollIntervalWithSalt() { + int positive = random.nextInt(5); + int sign = random.nextBoolean() ? 1 : -1; + long currentPollInterval = pollInterval + sign * positive * 1000; + return currentPollInterval > 0 ? currentPollInterval : pollInterval; + } + class PullInstanceOnceTask implements Task { @Override public void execute() { @@ -232,13 +235,15 @@ public void execute() { } private synchronized void pullAllInstance() { - List failedInstances = new ArrayList<>(); - instancesCache.forEach((k, v) -> failedInstances.addAll(pullInstance(k, v, true))); - if (failedInstances.isEmpty()) { - return; - } - failedInstances.forEach(instancesCache::remove); - failedInstances.clear(); + List emptyInstancesCache = new ArrayList<>(); + instancesCache.forEach((k, v) -> { + pullInstance(k, v, true); + if (v.instancesCache.isEmpty()) { + emptyInstancesCache.add(k); + } + }); + emptyInstancesCache.forEach(instancesCache::remove); + emptyInstancesCache.clear(); } private static String instanceToString(List instances) { @@ -252,10 +257,9 @@ private static String instanceToString(List instances) { sb.append(endpoint.length() > 64 ? endpoint.substring(0, 64) : endpoint); sb.append("|"); } - sb.append(instance.getServiceName()); + sb.append(instance.getStatus()); sb.append("|"); } - sb.append("#"); return sb.toString(); } } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterOperation.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterOperation.java index 5521a732244..fd39a4ce963 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterOperation.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterOperation.java @@ -23,6 +23,7 @@ import org.apache.servicecomb.service.center.client.exception.OperationException; import org.apache.servicecomb.service.center.client.model.CreateSchemaRequest; import org.apache.servicecomb.service.center.client.model.FindMicroserviceInstancesResponse; +import org.apache.servicecomb.service.center.client.model.Framework; import org.apache.servicecomb.service.center.client.model.HeartbeatsRequest; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; @@ -159,7 +160,6 @@ boolean updateMicroserviceInstanceStatus(String serviceId, String instanceId, * Batch send heartbeats to service-center * * @param heartbeatsRequest - * @return * @throws OperationException */ void sendHeartBeats(HeartbeatsRequest heartbeatsRequest); @@ -184,7 +184,7 @@ boolean updateMicroserviceInstanceStatus(String serviceId, String instanceId, * @return if heartbeat is successful * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ - RbacTokenResponse queryToken(RbacTokenRequest request); + RbacTokenResponse queryToken(RbacTokenRequest request, String host); /** * Update properties of microservice @@ -192,5 +192,11 @@ boolean updateMicroserviceInstanceStatus(String serviceId, String instanceId, * @return if update is successful * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ - boolean updateMicroserviceProperties(String microserviceId, Map serviceProperties); + boolean updateMicroserviceProperties(String microserviceId, Map serviceProperties, + Framework framework); + + /** + * Check serviceCenter isolation address available + */ + void checkIsolationAddressAvailable(); } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.java index 6c293004cc0..e233f15fb52 100755 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.java @@ -21,23 +21,33 @@ import java.util.HashMap; import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; import org.apache.servicecomb.http.client.common.HttpRequest; import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.http.client.common.HttpTransport; +import org.apache.servicecomb.http.client.event.OperationEvents.UnAuthorizedOperationEvent; +import org.apache.servicecomb.http.client.utils.ServiceCombServiceAvailableUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.eventbus.EventBus; + public class ServiceCenterRawClient { private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterRawClient.class); private static final String HEADER_TENANT_NAME = "x-domain-name"; + private static final String ADDRESS_CHECK_PATH = "/v4/default/registry/health/readiness"; + private final String tenantName; private final HttpTransport httpTransport; private final ServiceCenterAddressManager addressManager; + private EventBus eventBus; + private ServiceCenterRawClient(String tenantName, HttpTransport httpTransport, ServiceCenterAddressManager addressManager) { this.httpTransport = httpTransport; @@ -45,41 +55,40 @@ private ServiceCenterRawClient(String tenantName, HttpTransport httpTransport, this.addressManager = addressManager; } + public void setEventBus(EventBus eventBus) { + this.eventBus = eventBus; + } + public HttpResponse getHttpRequest(String url, Map headers, String content) throws IOException { - return doHttpRequest(url, false, headers, content, HttpRequest.GET); + return doHttpRequest(url, false, headers, content, HttpRequest.GET, ""); } - public HttpResponse postHttpRequestAbsoluteUrl(String url, Map headers, String content) - throws IOException { - return doHttpRequest(url, true, headers, content, HttpRequest.POST); + public HttpResponse postHttpRequestAbsoluteUrl(String url, Map headers, String content, + String address) throws IOException { + return doHttpRequest(url, true, headers, content, HttpRequest.POST, address); } public HttpResponse postHttpRequest(String url, Map headers, String content) throws IOException { - return doHttpRequest(url, false, headers, content, HttpRequest.POST); + return doHttpRequest(url, false, headers, content, HttpRequest.POST, ""); } public HttpResponse putHttpRequest(String url, Map headers, String content) throws IOException { - return doHttpRequest(url, false, headers, content, HttpRequest.PUT); + return doHttpRequest(url, false, headers, content, HttpRequest.PUT, ""); } public HttpResponse deleteHttpRequest(String url, Map headers, String content) throws IOException { - return doHttpRequest(url, false, headers, content, HttpRequest.DELETE); + return doHttpRequest(url, false, headers, content, HttpRequest.DELETE, ""); } private HttpResponse doHttpRequest(String url, boolean absoluteUrl, Map headers, String content, - String method) - throws IOException { - String address = addressManager.address(); + String method, String queryAddress) throws IOException { + String address = StringUtils.isEmpty(queryAddress) ? addressManager.address() : queryAddress; String formatUrl = addressManager.formatUrl(url, absoluteUrl, address); - if (headers == null) { - headers = new HashMap<>(); - } - headers.put(HEADER_TENANT_NAME, tenantName); - HttpRequest httpRequest = new HttpRequest(formatUrl, headers, content, method); - + HttpRequest httpRequest = buildHttpRequest(formatUrl, headers, content, method); + HttpResponse httpResponse; try { - HttpResponse httpResponse = httpTransport.doRequest(httpRequest); - addressManager.recordSuccessState(address); + httpResponse = httpTransport.doRequest(httpRequest); + recordAndSendUnAuthorizedEvent(httpResponse, address); return httpResponse; } catch (IOException e) { addressManager.recordFailState(address); @@ -88,7 +97,9 @@ private HttpResponse doHttpRequest(String url, boolean absoluteUrl, Map headers, String content, String method) { + if (headers == null) { + headers = new HashMap<>(); + } + headers.put(HEADER_TENANT_NAME, tenantName); + return new HttpRequest(url, headers, content, method); + } + public static class Builder { private String tenantName; diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRegistration.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRegistration.java index c9340e4d380..93988ea8948 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRegistration.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRegistration.java @@ -17,6 +17,7 @@ package org.apache.servicecomb.service.center.client; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -55,7 +56,7 @@ public class ServiceCenterRegistration extends AbstractTask { private MicroserviceInstance microserviceInstance; - private List schemaInfos; + private List schemaInfos = new ArrayList<>(); private final ServiceCenterConfiguration serviceCenterConfiguration; @@ -102,8 +103,18 @@ public ServiceCenterRegistration setSchemaInfos(List schemaInfos) { return this; } + public ServiceCenterRegistration addSchemaInfo(SchemaInfo schemaInfo) { + this.schemaInfos.add(schemaInfo); + return this; + } + + public List getSchemaInfos() { + return this.schemaInfos; + } + public void startRegistration() { startTask(new RegisterMicroserviceTask(0)); + schedulerCheckAddressAvailable("sc-addr-check", new CheckAddressTask(), heartBeatInterval); } class RegisterMicroserviceTask implements Task { @@ -137,13 +148,13 @@ public void execute() { microservice.setProperties(newMicroservice.getProperties()); microservice.getProperties().putAll(propertiesTemp); if (serviceCenterClient.updateMicroserviceProperties(serviceResponse.getServiceId(), - microservice.getProperties())) { + microservice.getProperties(), microservice.getFramework())) { LOGGER.info( - "microservice is already registered. Update microservice properties successfully. properties=[{}]", - microservice.getProperties()); + "microservice is already registered. Update microservice properties successfully. properties=[{}], " + + "frameworkVersion [{}]", microservice.getProperties(), microservice.getFramework().getVersion()); } else { - LOGGER.error("microservice is already registered. Update microservice properties failed. properties=[{}]", - microservice.getProperties()); + LOGGER.error("microservice is already registered. Update microservice properties failed. properties=[{}], " + + "frameworkVersion [{}]", microservice.getProperties(), microservice.getFramework().getVersion()); } microservice.setServiceId(serviceResponse.getServiceId()); @@ -276,7 +287,7 @@ public void execute() { } if (!serviceCenterClient.sendHeartBeat(microservice.getServiceId(), microserviceInstance.getInstanceId())) { - LOGGER.error("send heart failed, and will try again."); + LOGGER.warn("send heart failed, and will try again."); eventBus.post(new HeartBeatEvent(false, microservice, microserviceInstance)); startTask(new BackOffSleepTask(failedCount + 1, new SendHeartBeatTask(failedCount + 1))); } else { @@ -286,10 +297,22 @@ public void execute() { new BackOffSleepTask(Math.max(heartBeatInterval, heartBeatRequestTimeout), new SendHeartBeatTask(0))); } } catch (Exception e) { - LOGGER.error("send heart failed, and will try again.", e); + // If heartbeat failures three times, log error stack help troubleshooting. Others just log message as warn. + if (failedCount == 2) { + LOGGER.error("send heart failed, and will try again.", e); + } else { + LOGGER.warn("send heart failed, and will try again. message [{}]", e.getMessage()); + } eventBus.post(new HeartBeatEvent(false, microservice, microserviceInstance)); startTask(new BackOffSleepTask(failedCount + 1, new SendHeartBeatTask(failedCount + 1))); } } } + + class CheckAddressTask implements Runnable { + @Override + public void run() { + serviceCenterClient.checkIsolationAddressAvailable(); + } + } } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.java index a8be8b98b78..9f24a74a55a 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.java @@ -17,12 +17,15 @@ package org.apache.servicecomb.service.center.client; +import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.servicecomb.foundation.auth.SignRequest; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; import org.apache.servicecomb.http.client.common.WebSocketListener; @@ -67,7 +70,7 @@ public class ServiceCenterWatch implements WebSocketListener { private String serviceId; - private int continuousError = 0; + private AtomicInteger continuousError = new AtomicInteger(0); private final AtomicBoolean reconnecting = new AtomicBoolean(false); @@ -107,7 +110,7 @@ private void startWatch() { Map headers = new HashMap<>(); headers.put("x-domain-name", this.tenantName); headers.putAll(this.extraGlobalHeaders); - headers.putAll(this.requestAuthHeaderProvider.loadAuthHeader(null)); + headers.putAll(this.requestAuthHeaderProvider.loadAuthHeader(createSignRequest(address))); currentServerUri = convertAddress(address); LOGGER.info("start watch to address {}", currentServerUri); webSocketTransport = new WebSocketTransport(currentServerUri, sslProperties, @@ -121,6 +124,17 @@ private void startWatch() { }); } + private SignRequest createSignRequest(String url) { + try { + URI uri = URI.create(url); + SignRequest signRequest = new SignRequest(); + signRequest.setEndpoint(uri); + return signRequest; + } catch (Exception e) { + return null; + } + } + private String convertAddress(String address) { String url = String.format(WATCH, project, serviceId); if (address.startsWith(HTTP)) { @@ -143,7 +157,7 @@ private void reconnect() { if (reconnecting.getAndSet(true)) { return; } - continuousError++; + continuousError.incrementAndGet(); if (webSocketTransport != null) { webSocketTransport.close(); } @@ -151,11 +165,11 @@ private void reconnect() { } private void backOff() { - if (this.continuousError <= 0) { + if (this.continuousError.get() <= 0) { return; } try { - Thread.sleep(Math.min(SLEEP_MAX, this.continuousError * this.continuousError * SLEEP_BASE)); + Thread.sleep(Math.min(SLEEP_MAX, this.continuousError.get() * this.continuousError.get() * SLEEP_BASE)); } catch (InterruptedException e) { // do not care } @@ -183,7 +197,7 @@ public void onOpen(ServerHandshake serverHandshake) { LOGGER.info("web socket connected to server {}, status={}, message={}", currentServerUri, serverHandshake.getHttpStatus(), serverHandshake.getHttpStatusMessage()); - continuousError = 0; + continuousError.set(0); reconnecting.set(false); } } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/Microservice.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/Microservice.java index 1c2fc88a1db..a034e57569f 100755 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/Microservice.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/Microservice.java @@ -208,4 +208,8 @@ public List getPaths() { public void setPaths(List paths) { this.paths = paths; } + + public void addSchema(String schema) { + this.schemas.add(schema); + } } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstance.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstance.java index bbf6c595260..7f03c314043 100755 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstance.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstance.java @@ -147,6 +147,11 @@ public void setMicroservice(Microservice microservice) { this.microservice = microservice; } + @JsonIgnore + public Microservice getMicroservice() { + return this.microservice; + } + @JsonIgnore public String getServiceName() { return this.microservice.getServiceName(); @@ -156,4 +161,12 @@ public String getServiceName() { public String getApplicationName() { return this.microservice.getAppId(); } + + public void addEndpoint(String endpoint) { + this.endpoints.add(endpoint); + } + + public void addProperty(String key, String value) { + this.properties.put(key, value); + } } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/UpdatePropertiesRequest.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/UpdatePropertiesRequest.java index b10586208a8..d7d4b093930 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/UpdatePropertiesRequest.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/UpdatePropertiesRequest.java @@ -22,6 +22,8 @@ public class UpdatePropertiesRequest { private Map properties; + private Framework framework; + public Map getProperties() { return properties; } @@ -29,4 +31,12 @@ public Map getProperties() { public void setProperties(Map properties) { this.properties = properties; } + + public Framework getFramework() { + return framework; + } + + public void setFramework(Framework framework) { + this.framework = framework; + } } diff --git a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManagerTest.java b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManagerTest.java index f5c316cd848..a2888cfad1b 100644 --- a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManagerTest.java +++ b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManagerTest.java @@ -41,7 +41,7 @@ class ServiceCenterAddressManagerTest { @Test public void getUrlPrefix() { addresses.add("http://127.0.0.1:30103"); - addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus()); + addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus(), "", ""); Assertions.assertNotNull(addressManager1); @@ -55,7 +55,7 @@ public void getUrlPrefix() { @Test public void formatUrlTest() { addresses.add("http://127.0.0.1:30103"); - addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus()); + addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus(), "", ""); Assertions.assertNotNull(addressManager1); String address = addressManager1.address(); @@ -76,7 +76,7 @@ public void onRefreshEndpointEvent() { Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); - addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus()); + addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus(), "", ""); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "SERVICECENTER"); addressManager1.refreshEndpoint(event, "SERVICECENTER"); diff --git a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterClientTest.java b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterClientTest.java index 12ad9cda3ab..c486b2bd6ce 100755 --- a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterClientTest.java +++ b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterClientTest.java @@ -19,10 +19,12 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import org.apache.servicecomb.http.client.common.HttpResponse; +import org.apache.servicecomb.service.center.client.model.Framework; import org.apache.servicecomb.service.center.client.model.HeartbeatsRequest; import org.apache.servicecomb.service.center.client.model.InstancesRequest; import org.apache.servicecomb.service.center.client.model.Microservice; @@ -41,11 +43,18 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.google.common.eventbus.EventBus; /** * Created by on 2019/10/17. */ public class ServiceCenterClientTest { + private final ServiceCenterAddressManager addressManager; + + public ServiceCenterClientTest() { + this.addressManager = new ServiceCenterAddressManager("default", Arrays.asList("http://127.0.0.1:30100"), + new EventBus(), "", ""); + } @Test public void TestGetServiceCenterInstances() throws IOException { @@ -92,7 +101,7 @@ public void TestGetServiceCenterInstances() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest("/registry/health", null, null)).thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); MicroserviceInstancesResponse serviceCenterInstances = serviceCenterClient.getServiceCenterInstances(); Assertions.assertNotNull(serviceCenterInstances); @@ -121,7 +130,7 @@ public void TestRegistryService() throws IOException { Mockito.anyString())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); RegisteredMicroserviceResponse actualResponse = serviceCenterClient.registerMicroservice(microservice); Assertions.assertNotNull(actualResponse); @@ -179,7 +188,7 @@ public void TestGetServiceMessage() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest("/registry/microservices/111111", null, null)) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); Microservice microservices = serviceCenterClient.getMicroserviceByServiceId("111111"); Assertions.assertNotNull(microservices); @@ -207,7 +216,7 @@ public void TestGetServiceList() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); MicroservicesResponse actualMicroservicesResponse = serviceCenterClient.getMicroserviceList(); Assertions.assertNotNull(actualMicroservicesResponse); @@ -228,7 +237,7 @@ public void TestQueryServiceId() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); Microservice microservice = new Microservice("Test111"); RegisteredMicroserviceResponse actualServiceId = serviceCenterClient.queryServiceId(microservice); @@ -257,7 +266,7 @@ public void TestRegisterServiceInstance() throws IOException { Mockito.anyString())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); RegisteredMicroserviceInstanceResponse actualResponse = serviceCenterClient.registerMicroserviceInstance(instance); Assertions.assertNotNull(actualResponse); @@ -276,7 +285,7 @@ public void TestDeleteServiceInstance() throws IOException { Mockito.when(serviceCenterRawClient.deleteHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); serviceCenterClient.deleteMicroserviceInstance("111", "222"); } @@ -310,7 +319,7 @@ public void TestGetServiceInstanceList() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest("/registry/microservices/222222/instances", null, null)) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); MicroserviceInstancesResponse serviceCenterInstances = serviceCenterClient .getMicroserviceInstanceList("222222"); @@ -364,7 +373,7 @@ public void TestGetServiceInstanceMessage() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); MicroserviceInstance responseInstance = serviceCenterClient .getMicroserviceInstance("111", "222"); @@ -394,7 +403,7 @@ public void TestSendHeartBeats() throws IOException { Mockito.anyString())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); serviceCenterClient.sendHeartBeats(heartbeatsRequest); } @@ -410,7 +419,7 @@ public void TestUpdateServicesInstanceStatus() throws IOException { Mockito.when(serviceCenterRawClient.putHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); Boolean result = serviceCenterClient .updateMicroserviceInstanceStatus("111", "222", MicroserviceInstanceStatus.UP); @@ -440,7 +449,7 @@ public void TestGetServiceSchemas() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); List schemaResponse = serviceCenterClient .getServiceSchemasList("111", false); @@ -468,7 +477,7 @@ public void TestGetServiceSchemasContext() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); String schemaContext = serviceCenterClient .getServiceSchemaContext("111", "222"); @@ -488,7 +497,7 @@ public void TestUpdateServiceSchema() throws IOException { Mockito.when(serviceCenterRawClient.putHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); boolean result = serviceCenterClient .updateServiceSchemaContext("111", new SchemaInfo()); @@ -506,9 +515,9 @@ public void testUpdateMicroserviceProperties() throws IOException { Mockito.when(serviceCenterRawClient.putHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); boolean result = serviceCenterClient - .updateMicroserviceProperties("111", new HashMap()); + .updateMicroserviceProperties("111", new HashMap(), new Framework()); Assertions.assertTrue(result); } diff --git a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.java b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.java index 29337e31a44..48f3bd914c6 100755 --- a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.java +++ b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.java @@ -41,7 +41,8 @@ public class ServiceCenterRawClientTest { public void TestDefaultParameter() throws IOException { HttpTransport httpTransport = Mockito.mock(HttpTransport.class); - ServiceCenterAddressManager addressManager = new ServiceCenterAddressManager(PROJECT_NAME, Arrays.asList("http://127.0.0.1:30100"), new EventBus()); + ServiceCenterAddressManager addressManager = new ServiceCenterAddressManager(PROJECT_NAME, + Arrays.asList("http://127.0.0.1:30100"), new EventBus(), "", ""); ServiceCenterRawClient client = new ServiceCenterRawClient.Builder() .setHttpTransport(httpTransport) .setAddressManager(addressManager) diff --git a/common/common-access-log/pom.xml b/common/common-access-log/pom.xml index 1a12f622367..e572e6bfd94 100644 --- a/common/common-access-log/pom.xml +++ b/common/common-access-log/pom.xml @@ -21,7 +21,7 @@ common org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -41,6 +41,10 @@ org.apache.servicecomb common-rest + + org.apache.servicecomb + transport-rest-client + io.vertx vertx-codegen diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfig.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfig.java index ce2dc58856e..e2f204b3cad 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfig.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfig.java @@ -17,64 +17,60 @@ package org.apache.servicecomb.common.accessLog; -import com.netflix.config.DynamicPropertyFactory; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; public class AccessLogConfig { - private static final String SERVER_BASE = "servicecomb.accesslog."; + public static final String SERVER_BASE = "servicecomb.accesslog."; - private static final String CLIENT_BASE = "servicecomb.accesslog.request."; + public static final String CLIENT_BASE = "servicecomb.accesslog.request."; - private static final String SERVER_LOG_ENABLED = SERVER_BASE + "enabled"; + public static final String SERVER_LOG_ENABLED = SERVER_BASE + "enabled"; - private static final String SERVER_LOG_PATTERN = SERVER_BASE + "pattern"; + public static final String SERVER_LOG_PATTERN = SERVER_BASE + "pattern"; - private static final String CLIENT_LOG_ENABLED = CLIENT_BASE + "enabled"; + public static final String CLIENT_LOG_ENABLED = CLIENT_BASE + "enabled"; - private static final String CLIENT_LOG_PATTERN = CLIENT_BASE + "pattern"; + public static final String CLIENT_LOG_PATTERN = CLIENT_BASE + "pattern"; - private static final String DEFAULT_SERVER_PATTERN = "%h - - %t %r %s %B %D"; + public static final String DEFAULT_SERVER_PATTERN = "%h - - %t %r %s %B %D"; - private static final String DEFAULT_CLIENT_PATTERN = "%h %SCB-transport - - %t %r %s %D"; + public static final String DEFAULT_CLIENT_PATTERN = "%h %SCB-transport - - %t %r %s %D"; - public static final AccessLogConfig INSTANCE = new AccessLogConfig(); + public static final AccessLogConfig INSTANCE = new AccessLogConfig(); - private boolean serverLogEnabled; + private boolean serverLogEnabled; - private boolean clientLogEnabled; + private boolean clientLogEnabled; - private String serverLogPattern; + private String serverLogPattern; - private String clientLogPattern; + private String clientLogPattern; - private AccessLogConfig() { - init(); - } + private AccessLogConfig() { + init(); + } - private void init() { - clientLogEnabled = DynamicPropertyFactory - .getInstance().getBooleanProperty(CLIENT_LOG_ENABLED, false).get(); - serverLogEnabled = DynamicPropertyFactory - .getInstance().getBooleanProperty(SERVER_LOG_ENABLED, false).get(); - clientLogPattern = DynamicPropertyFactory - .getInstance().getStringProperty(CLIENT_LOG_PATTERN, DEFAULT_CLIENT_PATTERN).get(); - serverLogPattern = DynamicPropertyFactory - .getInstance().getStringProperty(SERVER_LOG_PATTERN, DEFAULT_SERVER_PATTERN).get(); - } + private void init() { + clientLogEnabled = LegacyPropertyFactory.getBooleanProperty(CLIENT_LOG_ENABLED, false); + serverLogEnabled = LegacyPropertyFactory.getBooleanProperty(SERVER_LOG_ENABLED, false); + clientLogPattern = LegacyPropertyFactory.getStringProperty(CLIENT_LOG_PATTERN, DEFAULT_CLIENT_PATTERN); + serverLogPattern = LegacyPropertyFactory.getStringProperty(SERVER_LOG_PATTERN, DEFAULT_SERVER_PATTERN); + } - public boolean isServerLogEnabled() { - return serverLogEnabled; - } + public boolean isServerLogEnabled() { + return serverLogEnabled; + } - public boolean isClientLogEnabled() { - return clientLogEnabled; - } + public boolean isClientLogEnabled() { + return clientLogEnabled; + } - public String getServerLogPattern() { - return serverLogPattern; - } + public String getServerLogPattern() { + return serverLogPattern; + } - public String getClientLogPattern() { - return clientLogPattern; - } + public String getClientLogPattern() { + return clientLogPattern; + } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfiguration.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfiguration.java new file mode 100644 index 00000000000..abbb90adc19 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfiguration.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.common.accessLog; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AccessLogConfiguration { + @Bean + public AccessLogBootListener scbAccessLogBootListener() { + return new AccessLogBootListener(); + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/client/ClientDefaultInitializer.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/client/ClientDefaultInitializer.java index 658cea9fa55..43e5ad9b191 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/client/ClientDefaultInitializer.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/client/ClientDefaultInitializer.java @@ -21,7 +21,6 @@ import org.apache.servicecomb.common.accessLog.AccessLogInitializer; import org.apache.servicecomb.common.accessLog.core.AccessLogGenerator; import org.apache.servicecomb.core.event.InvocationFinishEvent; - import org.apache.servicecomb.swagger.invocation.InvocationType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,7 +30,7 @@ import com.google.common.eventbus.Subscribe; public class ClientDefaultInitializer implements AccessLogInitializer { - private static final Logger LOGGER = LoggerFactory.getLogger("requestlog"); + private static final Logger LOGGER = LoggerFactory.getLogger("scb-access"); private AccessLogGenerator accessLogGenerator; @@ -48,7 +47,7 @@ public void init(EventBus eventBus, AccessLogConfig accessLogConfig) { @AllowConcurrentEvents public void onRequestOut(InvocationFinishEvent finishEvent) { if (InvocationType.CONSUMER.equals(finishEvent.getInvocation().getInvocationType())) { - LOGGER.info(accessLogGenerator.generateClientLog(finishEvent)); + LOGGER.info(accessLogGenerator.generateClientLog(finishEvent)); } } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ConfigurableDatetimeAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ConfigurableDatetimeAccessItem.java index d21ee86a3fa..9c759ca2cb5 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ConfigurableDatetimeAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ConfigurableDatetimeAccessItem.java @@ -58,7 +58,7 @@ public ConfigurableDatetimeAccessItem() { * @param config the format of configuration is "PATTERN|TIMEZONE|LOCALE" or "PATTERN". It depends on whether the config contains the separator "|" */ public ConfigurableDatetimeAccessItem(String config) { - String[] configArr = null; + String[] configArr; if (config.contains("|")) { configArr = splitConfig(config); } else { @@ -71,14 +71,14 @@ public ConfigurableDatetimeAccessItem(String config) { "wrong format of configuration, \"PATTERN|TIMEZONE|LOCALE\" is expected, but actually is \"" + config + "\""); } - setConfigruations(configArr); + setConfigurations(configArr); } private String[] splitConfig(String config) { return config.split("\\|{1}?", -1); } - private void setConfigruations(String[] configArr) { + private void setConfigurations(String[] configArr) { this.pattern = StringUtils.isEmpty(configArr[0]) ? DEFAULT_DATETIME_PATTERN : configArr[0]; this.timezone = StringUtils.isEmpty(configArr[1]) ? TimeZone.getDefault() : TimeZone.getTimeZone(configArr[1]); this.locale = StringUtils.isEmpty(configArr[2]) ? DEFAULT_LOCALE : Locale.forLanguageTag(configArr[2]); @@ -91,10 +91,9 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - long milliDuration = (finishEvent.getInvocation().getInvocationStageTrace().getStartSend() - - finishEvent.getInvocation().getInvocationStageTrace().getStart()) / 1000_000; + long milliDuration = finishEvent.getInvocation().getInvocationStageTrace().calcTotal() / 1000_000; doAppendFormattedItem( - finishEvent.getInvocation().getInvocationStageTrace().getStartTimeMillis() + milliDuration, builder); + finishEvent.getInvocation().getInvocationStageTrace().getStartInMillis() + milliDuration, builder); } private void doAppendFormattedItem(long milliStartTime, StringBuilder builder) { diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieAccessItem.java index 37f7dc5564e..e15179acc69 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieAccessItem.java @@ -22,9 +22,9 @@ import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.http.Cookie; import io.vertx.ext.web.RoutingContext; @@ -57,8 +57,8 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) finishEvent.getInvocation().getHandlerContext() - .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation().getHandlerContext() + .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); if (null == restRequestImpl || null == restRequestImpl.getCookieMap()) { builder.append(RESULT_NOT_FOUND); return; diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondAccessItem.java index e0bde4ad306..49ecda73d94 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondAccessItem.java @@ -31,7 +31,6 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - builder.append((finishEvent.getInvocation().getInvocationStageTrace().getFinish() - - finishEvent.getInvocation().getInvocationStageTrace().getStartSend()) / 1000_000); + builder.append(finishEvent.getInvocation().getInvocationStageTrace().calcTotal() / 1000_000); } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondAccessItem.java index 2da34026d5e..68d2ba0d411 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondAccessItem.java @@ -33,7 +33,6 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - builder.append((finishEvent.getInvocation().getInvocationStageTrace().getFinish() - - finishEvent.getInvocation().getInvocationStageTrace().getStartSend()) / 1000_000_000); + builder.append(finishEvent.getInvocation().getInvocationStageTrace().calcTotal() / 1000_000_000); } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodAccessItem.java index 8a59112b89a..8c8b61583e3 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodAccessItem.java @@ -21,7 +21,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; +import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; @@ -53,13 +53,13 @@ public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringB builder.append(operationMeta.getHttpMethod()); return; } - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) finishEvent.getInvocation().getHandlerContext() + RestClientRequest restRequestImpl = (RestClientRequest) finishEvent.getInvocation().getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); - if (null == restRequestImpl || null == restRequestImpl.getRequest() - || null == restRequestImpl.getRequest().getMethod()) { + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || null == restRequestImpl.getHttpClientRequest().getMethod()) { builder.append(EMPTY_RESULT); return; } - builder.append(restRequestImpl.getRequest().getMethod().toString()); + builder.append(restRequestImpl.getHttpClientRequest().getMethod().toString()); } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpStatusAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpStatusAccessItem.java index f4e8770bd06..61be5a790a6 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpStatusAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpStatusAccessItem.java @@ -21,15 +21,11 @@ import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.swagger.invocation.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; public class HttpStatusAccessItem implements AccessLogItem { - private static final Logger LOGGER = LoggerFactory.getLogger(HttpStatusAccessItem.class); - public static final String EMPTY_RESULT = "-"; @Override @@ -40,9 +36,6 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin return; } if (response.closed() && !response.ended()) { - LOGGER.warn( - "Response is closed before sending any data. " - + "Please check idle connection timeout for provider is properly configured."); builder.append(EMPTY_RESULT); return; } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostAccessItem.java index ef070c685d3..cfa2068b297 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostAccessItem.java @@ -21,7 +21,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; +import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; @@ -41,16 +41,16 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin */ @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) finishEvent.getInvocation().getHandlerContext() + RestClientRequest restRequestImpl = (RestClientRequest) finishEvent.getInvocation().getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); - if (null == restRequestImpl || null == restRequestImpl.getRequest() - || null == restRequestImpl.getRequest().connection() - || null == restRequestImpl.getRequest().connection().localAddress() - || StringUtils.isEmpty(restRequestImpl.getRequest().connection().localAddress().host())) { + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || null == restRequestImpl.getHttpClientRequest().connection() + || null == restRequestImpl.getHttpClientRequest().connection().localAddress() + || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().connection().localAddress().host())) { builder.append(EMPTY_RESULT); return; } - builder.append(restRequestImpl.getRequest().connection().localAddress().host()); + builder.append(restRequestImpl.getHttpClientRequest().connection().localAddress().host()); } public static String getLocalAddress(RoutingContext context) { diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortAccessItem.java index ffbc983abaf..def97c325be 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortAccessItem.java @@ -20,9 +20,9 @@ import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; @@ -43,14 +43,15 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) finishEvent.getInvocation().getHandlerContext() + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation() + .getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); - if (null == restRequestImpl || null == restRequestImpl.getRequest() - || null == restRequestImpl.getRequest().connection() - || null == restRequestImpl.getRequest().connection().localAddress()) { + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || null == restRequestImpl.getHttpClientRequest().connection() + || null == restRequestImpl.getHttpClientRequest().connection().localAddress()) { builder.append(EMPTY_RESULT); return; } - builder.append(restRequestImpl.getRequest().connection().localAddress().port()); + builder.append(restRequestImpl.getHttpClientRequest().connection().localAddress().port()); } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringAccessItem.java index 6ab7eb4a60d..e2493bd3c09 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringAccessItem.java @@ -20,9 +20,9 @@ import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; @@ -42,13 +42,14 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) finishEvent.getInvocation().getHandlerContext() + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation() + .getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); - if (null == restRequestImpl || null == restRequestImpl.getRequest() - || StringUtils.isEmpty(restRequestImpl.getRequest().query())) { + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().query())) { builder.append(EMPTY_RESULT); return; } - builder.append(restRequestImpl.getRequest().query()); + builder.append(restRequestImpl.getHttpClientRequest().query()); } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderAccessItem.java index 61b3f88f9da..54099d3b4b1 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderAccessItem.java @@ -20,9 +20,9 @@ import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.MultiMap; import io.vertx.ext.web.RoutingContext; @@ -48,15 +48,16 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) { - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) clientLogEvent.getInvocation().getHandlerContext() + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) clientLogEvent.getInvocation() + .getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); - if (null == restRequestImpl || null == restRequestImpl.getRequest() - || null == restRequestImpl.getRequest().headers() - || StringUtils.isEmpty(restRequestImpl.getRequest().headers().get(varName))) { + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || null == restRequestImpl.getHttpClientRequest().headers() + || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().headers().get(varName))) { builder.append(RESULT_NOT_FOUND); return; } - builder.append(restRequestImpl.getRequest().headers().get(varName)); + builder.append(restRequestImpl.getHttpClientRequest().headers().get(varName)); } public String getVarName() { diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/TraceIdAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/TraceIdAccessItem.java index 5940ffc0484..e9dfe16d018 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/TraceIdAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/TraceIdAccessItem.java @@ -18,14 +18,14 @@ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; public class TraceIdAccessItem extends InvocationContextAccessItem { - public static final String TRACE_ID = Const.TRACE_ID_NAME; + public static final String TRACE_ID = CoreConst.TRACE_ID_NAME; public TraceIdAccessItem() { super(TRACE_ID); diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathAccessItem.java index ff999da04c3..5435a22cfb1 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathAccessItem.java @@ -21,11 +21,11 @@ import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; @@ -49,16 +49,17 @@ public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringB OperationMeta operationMeta = finishEvent.getInvocation().getOperationMeta(); SchemaMeta schemaMeta = finishEvent.getInvocation().getSchemaMeta(); if (operationMeta != null && schemaMeta != null && schemaMeta.getSwagger() != null) { - builder.append(schemaMeta.getSwagger().getBasePath()).append(operationMeta.getOperationPath()); + builder.append(operationMeta.getOperationPath()); return; } - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) finishEvent.getInvocation().getHandlerContext() + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation() + .getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); - if (null == restRequestImpl || null == restRequestImpl.getRequest() - || StringUtils.isEmpty(restRequestImpl.getRequest().path())) { + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().path())) { builder.append(EMPTY_RESULT); return; } - builder.append(restRequestImpl.getRequest().path()); + builder.append(restRequestImpl.getHttpClientRequest().path()); } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/impl/VertxRestAccessLogPatternParser.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/impl/VertxRestAccessLogPatternParser.java index 7a5b6fb6df3..7a8568b97f7 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/impl/VertxRestAccessLogPatternParser.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/impl/VertxRestAccessLogPatternParser.java @@ -22,7 +22,6 @@ import java.util.Iterator; import java.util.List; -import com.google.common.annotations.VisibleForTesting; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.accessLog.core.element.impl.PlainTextAccessItem; import org.apache.servicecomb.common.accessLog.core.parser.AccessLogItemMeta; @@ -33,6 +32,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.annotations.VisibleForTesting; + import io.vertx.ext.web.RoutingContext; /** @@ -173,7 +174,6 @@ private List matchAccessLogItem(String rawPattern) { AccessLogItemLocation candidate = null; for (VertxRestAccessLogItemMeta meta : metaList) { if (null != candidate && null == meta.getSuffix()) { - // TODO: // if user define item("%{","}ab") and item("%{_","}abc") and the pattern is "%{_var}ab}abc" // currently the result is item("%{","_var","}ab"), plaintext("}abc") // is this acceptable? diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/server/ServerDefaultInitializer.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/server/ServerDefaultInitializer.java index c528b67f9b9..7baf69dd8aa 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/server/ServerDefaultInitializer.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/server/ServerDefaultInitializer.java @@ -31,7 +31,7 @@ public class ServerDefaultInitializer implements AccessLogInitializer { - private static final Logger LOGGER = LoggerFactory.getLogger("accesslog"); + private static final Logger LOGGER = LoggerFactory.getLogger("scb-access"); private AccessLogGenerator accessLogGenerator; diff --git a/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta b/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta index 78f937b61fe..f793ca0f575 100644 --- a/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta +++ b/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.common.accessLog.core.parser.impl.DefaultCompositeVertxRestAccessLogItemMeta \ No newline at end of file +org.apache.servicecomb.common.accessLog.core.parser.impl.DefaultCompositeVertxRestAccessLogItemMeta diff --git a/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.core.BootListener b/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.core.BootListener deleted file mode 100644 index 7ab357ee11c..00000000000 --- a/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.core.BootListener +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.common.accessLog.AccessLogBootListener \ No newline at end of file diff --git a/common/common-access-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/common/common-access-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..e6814e63814 --- /dev/null +++ b/common/common-access-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.common.accessLog.AccessLogConfiguration diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogConfigTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogConfigTest.java deleted file mode 100644 index 964cc6d0e8d..00000000000 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogConfigTest.java +++ /dev/null @@ -1,42 +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.servicecomb.common.accessLog.core; - -import org.apache.servicecomb.common.accessLog.AccessLogConfig; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class AccessLogConfigTest { - - @Test - public void getAccessLogEnabled() { - boolean serverEnabled = AccessLogConfig.INSTANCE.isServerLogEnabled(); - boolean clientEnabled = AccessLogConfig.INSTANCE.isClientLogEnabled(); - Assertions.assertFalse(serverEnabled); - Assertions.assertFalse(clientEnabled); - } - - @Test - public void getAccessLogPattern() { - String clientLogPattern = AccessLogConfig.INSTANCE.getClientLogPattern(); - String serverLogPattern = AccessLogConfig.INSTANCE.getServerLogPattern(); - - Assertions.assertEquals("%h - - %t %r %s %B %D", serverLogPattern); - Assertions.assertEquals("%h %SCB-transport - - %t %r %s %D", clientLogPattern); - } -} diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogGeneratorTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogGeneratorTest.java index ee26be04cee..e8c7cf38500 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogGeneratorTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogGeneratorTest.java @@ -81,10 +81,8 @@ public void testClientLog() { InvocationStageTrace stageTrace = Mockito.mock(InvocationStageTrace.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); long startMillisecond = 1416863450581L; - when(stageTrace.getStartSend()).thenReturn(0L); - when(stageTrace.getStart()).thenReturn(0L); - when(stageTrace.getFinish()).thenReturn(0L); - when(stageTrace.getStartTimeMillis()).thenReturn(startMillisecond); + when(stageTrace.getStartInMillis()).thenReturn(startMillisecond); + when(stageTrace.calcTotal()).thenReturn(0L); when(invocation.getOperationMeta()).thenReturn(operationMeta); when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieItemTest.java index 5f96b175628..f1d37e9681f 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieItemTest.java @@ -23,18 +23,18 @@ import java.util.HashSet; import java.util.Map; -import io.vertx.core.http.HttpServerRequest; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.Cookie; +import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.impl.CookieImpl; import io.vertx.ext.web.RoutingContext; @@ -58,7 +58,7 @@ public class CookieItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; @BeforeEach public void initStrBuilder() { @@ -66,7 +66,7 @@ public void initStrBuilder() { httpServerRequest = Mockito.mock(HttpServerRequest.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); accessLogEvent = new ServerAccessLogEvent(); strBuilder = new StringBuilder(); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DatetimeConfigurableItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DatetimeConfigurableItemTest.java index 750ebde7e39..8f55eb0006c 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DatetimeConfigurableItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DatetimeConfigurableItemTest.java @@ -30,10 +30,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledOnJre; -import org.junit.jupiter.api.condition.EnabledOnOs; -import org.junit.jupiter.api.condition.JRE; -import org.junit.jupiter.api.condition.OS; import org.mockito.Mockito; public class DatetimeConfigurableItemTest { @@ -58,61 +54,19 @@ public void initStrBuilder() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); - when(invocationStageTrace.getStartSend()).thenReturn(0L); - when(invocationStageTrace.getStart()).thenReturn(0L); - when(invocationStageTrace.getStartTimeMillis()).thenReturn(START_MILLISECOND); + when(invocationStageTrace.getStartInMillis()).thenReturn(START_MILLISECOND); + when(invocationStageTrace.calcTotal()).thenReturn(0L); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setMilliStartTime(START_MILLISECOND); strBuilder = new StringBuilder(); } - @Test - @EnabledOnOs({OS.LINUX, OS.WINDOWS}) - @EnabledOnJre(JRE.JAVA_8) - public void serverFormattedElement() { - ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "EEE, yyyy MMM dd HH:mm:ss zzz|GMT-08|zh-CN"); - element.appendServerFormattedItem(accessLogEvent, strBuilder); - Assertions.assertEquals("星期一, 2014 十一月 24 13:10:50 GMT-08:00", strBuilder.toString()); - } - - @Test - @EnabledOnOs({OS.LINUX, OS.WINDOWS}) - @EnabledOnJre(JRE.JAVA_8) - public void clientFormattedElement() { - ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "EEE, yyyy MMM dd HH:mm:ss zzz|GMT-08|zh-CN"); - element.appendClientFormattedItem(finishEvent, strBuilder); - Assertions.assertEquals("星期一, 2014 十一月 24 13:10:50 GMT-08:00", strBuilder.toString()); - } - - @Test - @EnabledOnOs({OS.LINUX, OS.WINDOWS}) - @EnabledOnJre(JRE.JAVA_8) - public void serverFormattedElementOnNoPattern() { - ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "|GMT+08|zh-CN"); - - element.appendServerFormattedItem(accessLogEvent, strBuilder); - Assertions.assertEquals("星期二, 25 十一月 2014 05:10:50 GMT+08:00", strBuilder.toString()); - } - - @Test - @EnabledOnOs({OS.LINUX, OS.WINDOWS}) - @EnabledOnJre(JRE.JAVA_8) - public void clientFormattedElementOnNoPattern() { - ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "|GMT+08|zh-CN"); - - element.appendClientFormattedItem(finishEvent, strBuilder); - Assertions.assertEquals("星期二, 25 十一月 2014 05:10:50 GMT+08:00", strBuilder.toString()); - } @Test public void getFormattedElementOnNoTimezone() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "yyyy/MM/dd zzz||zh-CN"); + "yyyy/MM/dd zzz||zh-CN"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd zzz", Locale.forLanguageTag("zh-CN")); simpleDateFormat.setTimeZone(TimeZone.getDefault()); @@ -123,7 +77,7 @@ public void getFormattedElementOnNoTimezone() { @Test public void clientFormattedElementOnNoTimezone() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "yyyy/MM/dd zzz||zh-CN"); + "yyyy/MM/dd zzz||zh-CN"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd zzz", Locale.forLanguageTag("zh-CN")); simpleDateFormat.setTimeZone(TimeZone.getDefault()); @@ -134,7 +88,7 @@ public void clientFormattedElementOnNoTimezone() { @Test public void serverFormattedElementOnNoLocale() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|"); + "EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|"); element.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("Tue, 25 Nov 2014 05:10:50 GMT+08:00", strBuilder.toString()); @@ -143,7 +97,7 @@ public void serverFormattedElementOnNoLocale() { @Test public void clientFormattedElementOnNoLocale() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|"); + "EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|"); element.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("Tue, 25 Nov 2014 05:10:50 GMT+08:00", strBuilder.toString()); @@ -152,9 +106,9 @@ public void clientFormattedElementOnNoLocale() { @Test public void serverFormattedElementOnNoConfig() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "||"); + "||"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, - Locale.US); + Locale.US); simpleDateFormat.setTimeZone(TimeZone.getDefault()); element.appendServerFormattedItem(accessLogEvent, strBuilder); @@ -164,9 +118,9 @@ public void serverFormattedElementOnNoConfig() { @Test public void clientFormattedElementOnNoConfig() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "||"); + "||"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, - Locale.US); + Locale.US); simpleDateFormat.setTimeZone(TimeZone.getDefault()); element.appendClientFormattedItem(finishEvent, strBuilder); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondItemTest.java index 7ad32b23f87..1fdd3b4e0d0 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondItemTest.java @@ -50,8 +50,7 @@ public void initStrBuilder() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); - when(invocationStageTrace.getStartSend()).thenReturn(0L); - when(invocationStageTrace.getFinish()).thenReturn(1000_000L); + when(invocationStageTrace.calcTotal()).thenReturn(1000_000L); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setMilliStartTime(1L); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondItemTest.java index 3081ce51368..9cbe7f28da5 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondItemTest.java @@ -50,7 +50,7 @@ public void initStrBuilder() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); - when(invocationStageTrace.getStartSend()).thenReturn(1000_000L); + when(invocationStageTrace.calcTotal()).thenReturn(1L); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setMilliStartTime(1L); @@ -66,7 +66,7 @@ public void serverFormattedElementOn999ms() { @Test public void clientFormattedElementOn999ms() { - when(invocationStageTrace.getFinish()).thenReturn(1000_000_000L); + when(invocationStageTrace.calcTotal()).thenReturn(0L); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("0", strBuilder.toString()); } @@ -80,7 +80,7 @@ public void serverFormattedElementOn1000ms() { @Test public void clientFormattedElementOn1000ms() { - when(invocationStageTrace.getFinish()).thenReturn(1001_000_000L); + when(invocationStageTrace.calcTotal()).thenReturn(1000_000_000L); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("1", strBuilder.toString()); } @@ -94,7 +94,7 @@ public void serverFormattedElementOn1001ms() { @Test public void clientFormattedElementOn1001ms() { - when(invocationStageTrace.getFinish()).thenReturn(1002_000_000L); + when(invocationStageTrace.calcTotal()).thenReturn(1000_000_000L); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("1", strBuilder.toString()); } diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/FirstLineOfRequestItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/FirstLineOfRequestItemTest.java index 4d75a0befd5..54e1fccef33 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/FirstLineOfRequestItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/FirstLineOfRequestItemTest.java @@ -23,12 +23,12 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -54,7 +54,7 @@ public class FirstLineOfRequestItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; @@ -67,7 +67,7 @@ public void initStrBuilder() { mockContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); endpoint = Mockito.mock(Endpoint.class); urlEndpoint = Mockito.mock(URIEndpointObject.class); @@ -75,7 +75,7 @@ public void initStrBuilder() { handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerMap); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getAddress()).thenReturn(urlEndpoint); accessLogEvent = new ServerAccessLogEvent(); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodItemTest.java index 39a4212df96..1dbeba59e9a 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodItemTest.java @@ -23,12 +23,12 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -52,7 +52,7 @@ public class HttpMethodItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; @@ -65,7 +65,7 @@ public void initStrBuilder() { routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); endpoint = Mockito.mock(Endpoint.class); urlEndpoint = Mockito.mock(URIEndpointObject.class); @@ -92,7 +92,7 @@ public void serverFormattedElement() { @Test public void clientFormattedElement() { - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.getMethod()).thenReturn(HttpMethod.DELETE); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("DELETE", strBuilder.toString()); @@ -108,7 +108,7 @@ public void serverFormattedElementOnRequestIsNull() { @Test public void clientFormattedElementOnRequestIsNull() { - when(restClientRequest.getRequest()).thenReturn(null); + when(restClientRequest.getHttpClientRequest()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostItemTest.java index e4b936807ef..d1669f0316c 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostItemTest.java @@ -23,10 +23,10 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -55,7 +55,7 @@ public class LocalHostItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; @@ -69,7 +69,7 @@ public void initStrBuilder() { serverRequest = Mockito.mock(HttpServerRequest.class); socketAddress = Mockito.mock(SocketAddress.class); invocation = Mockito.mock(Invocation.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); connection = Mockito.mock(HttpConnection.class); Map handlerMap = new HashMap<>(); @@ -83,7 +83,7 @@ public void initStrBuilder() { @Test public void clientFormattedItem() { String localAddress = "192.168.0.1"; - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(socketAddress); when(socketAddress.host()).thenReturn(localAddress); @@ -119,7 +119,7 @@ public void serverLocalAddressOnRequestIsNull() { @Test public void clientLocalAddressOnRequestIsNull() { - when(restClientRequest.getRequest()).thenReturn(null); + when(restClientRequest.getHttpClientRequest()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @@ -134,7 +134,7 @@ public void serverLocalAddressOnLocalAddressIsNull() { @Test public void clientLocalAddressOnLocalAddressIsNull() { - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); @@ -153,7 +153,7 @@ public void serverLocalAddressOnHostIsNull() { @Test public void clientLocalAddressOnHostIsNull() { - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(socketAddress); when(socketAddress.host()).thenReturn(null); @@ -174,7 +174,7 @@ public void serverLocalAddressIsEmpty() { @Test public void clientLocalAddressIsEmpty() { - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(socketAddress); when(socketAddress.host()).thenReturn(""); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortItemTest.java index 430b5147daa..f77c51fbe74 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortItemTest.java @@ -23,10 +23,10 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -55,7 +55,7 @@ public class LocalPortItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; @@ -69,7 +69,7 @@ public void initStrBuilder() { serverRequest = Mockito.mock(HttpServerRequest.class); socketAddress = Mockito.mock(SocketAddress.class); invocation = Mockito.mock(Invocation.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); connection = Mockito.mock(HttpConnection.class); Map handlerMap = new HashMap<>(); @@ -92,7 +92,7 @@ public void serverFormattedElement() { @Test public void clientFormattedElement() { - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(socketAddress); when(socketAddress.port()).thenReturn(8080); @@ -109,7 +109,7 @@ public void serverFormattedElementOnRequestIsNull() { @Test public void clientFormattedElementOnRequestIsNull() { - when(restClientRequest.getRequest()).thenReturn(null); + when(restClientRequest.getHttpClientRequest()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @@ -124,7 +124,7 @@ public void serverFormattedElementOnLocalAddressIsNull() { @Test public void clientFormattedElementOnLocalAddressIsNull() { - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringItemTest.java index d2af7f57710..ef472623d43 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringItemTest.java @@ -23,10 +23,10 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -49,7 +49,7 @@ public class QueryStringItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpServerRequest serverRequest; @@ -61,7 +61,7 @@ public void initStrBuilder() { finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); serverRequest = Mockito.mock(HttpServerRequest.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); accessLogEvent = new ServerAccessLogEvent(); @@ -85,7 +85,7 @@ public void clientFormattedElement() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.query()).thenReturn(query); ITEM.appendClientFormattedItem(finishEvent, strBuilder); @@ -105,7 +105,7 @@ public void clientFormattedElementOnRequestIsNull() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(null); + when(restClientRequest.getHttpClientRequest()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); @@ -125,7 +125,7 @@ public void clientFormattedElementOnQueryIsNull() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.query()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); @@ -148,7 +148,7 @@ public void clientFormattedElementOnQueryIsEmpty() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.query()).thenReturn(query); ITEM.appendClientFormattedItem(finishEvent, strBuilder); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderItemTest.java index ed425d1f801..f2ae7004251 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderItemTest.java @@ -23,10 +23,10 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -53,7 +53,7 @@ public class RequestHeaderItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpServerRequest serverRequest; @@ -67,7 +67,7 @@ public void initStrBuilder() { finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); serverRequest = Mockito.mock(HttpServerRequest.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); headers = Mockito.mock(MultiMap.class); @@ -78,7 +78,7 @@ public void initStrBuilder() { @Test public void serverFormattedElement() { - HeadersMultiMap headers = new HeadersMultiMap(); + HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); String testValue = "testValue"; headers.add(VAR_NAME, testValue); when(routingContext.request()).thenReturn(serverRequest); @@ -97,7 +97,7 @@ public void clientFormattedElement() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.headers()).thenReturn(headers); when(headers.get(VAR_NAME)).thenReturn(testValue); @@ -120,7 +120,7 @@ public void clientFormattedElementIfHeaderIsNull() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.headers()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); @@ -129,7 +129,7 @@ public void clientFormattedElementIfHeaderIsNull() { @Test public void serverFormattedElementIfNotFound() { - HeadersMultiMap headers = new HeadersMultiMap(); + HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); String testValue = "testValue"; headers.add("anotherKey", testValue); when(routingContext.request()).thenReturn(serverRequest); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderItemTest.java index a2e283e4bc8..a8708172945 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderItemTest.java @@ -64,7 +64,7 @@ public void initStrBuilder() { @Test public void serverFormattedElement() { - HeadersMultiMap headers = new HeadersMultiMap(); + HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); String headerValue = "headerValue"; headers.add(VAR_NAME, headerValue); when(routingContext.response()).thenReturn(serverResponse); @@ -122,7 +122,7 @@ public void clientFormattedElementOnResponseIsNull() { @Test public void serverFormattedElementOnNotFound() { - HeadersMultiMap headers = new HeadersMultiMap(); + HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); String headerValue = "headerValue"; headers.add("anotherHeader", headerValue); when(routingContext.response()).thenReturn(serverResponse); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/TraceIdItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/TraceIdItemTest.java index 3c4ceb15e47..b21dc7dfbaf 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/TraceIdItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/TraceIdItemTest.java @@ -24,7 +24,7 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; @@ -70,7 +70,7 @@ public void initStrBuilder() { public void serverGetFormattedElementFromInvocationContext() { Map data = new HashMap<>(); String traceIdTest = "traceIdTest"; - when(invocation.getContext(Const.TRACE_ID_NAME)).thenReturn(traceIdTest); + when(invocation.getContext(CoreConst.TRACE_ID_NAME)).thenReturn(traceIdTest); when(routingContext.data()).thenReturn(data); data.put(RestConst.REST_INVOCATION_CONTEXT, invocation); @@ -81,7 +81,7 @@ public void serverGetFormattedElementFromInvocationContext() { @Test public void clientGetFormattedElementFromInvocationContext() { String traceIdTest = "traceIdTest"; - clientContext.put(Const.TRACE_ID_NAME, traceIdTest); + clientContext.put(CoreConst.TRACE_ID_NAME, traceIdTest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getContext()).thenReturn(clientContext); @@ -93,11 +93,11 @@ public void clientGetFormattedElementFromInvocationContext() { public void serverGetFormattedElementFromRequestHeader() { Map data = new HashMap<>(); String traceIdTest = "traceIdTest"; - when(invocation.getContext(Const.TRACE_ID_NAME)).thenReturn(null); + when(invocation.getContext(CoreConst.TRACE_ID_NAME)).thenReturn(null); when(routingContext.data()).thenReturn(data); data.put(RestConst.REST_INVOCATION_CONTEXT, invocation); - when(serverRequest.getHeader(Const.TRACE_ID_NAME)).thenReturn(traceIdTest); + when(serverRequest.getHeader(CoreConst.TRACE_ID_NAME)).thenReturn(traceIdTest); when(routingContext.request()).thenReturn(serverRequest); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), is(traceIdTest)); @@ -106,24 +106,24 @@ public void serverGetFormattedElementFromRequestHeader() { @Test public void serverGetFormattedElementOnTraceIdNotFound() { Map data = new HashMap<>(); - when(invocation.getContext(Const.TRACE_ID_NAME)).thenReturn(""); + when(invocation.getContext(CoreConst.TRACE_ID_NAME)).thenReturn(""); when(routingContext.data()).thenReturn(data); data.put(RestConst.REST_INVOCATION_CONTEXT, invocation); - when(serverRequest.getHeader(Const.TRACE_ID_NAME)).thenReturn(null); + when(serverRequest.getHeader(CoreConst.TRACE_ID_NAME)).thenReturn(null); when(routingContext.request()).thenReturn(serverRequest); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), is("-")); strBuilder = new StringBuilder(); - when(invocation.getContext(Const.TRACE_ID_NAME)).thenReturn(null); + when(invocation.getContext(CoreConst.TRACE_ID_NAME)).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), is("-")); } @Test public void clientGetFormattedElementOnTraceIdNotFound() { - clientContext.put(Const.TRACE_ID_NAME, null); + clientContext.put(CoreConst.TRACE_ID_NAME, null); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getContext()).thenReturn(clientContext); @@ -135,7 +135,7 @@ public void clientGetFormattedElementOnTraceIdNotFound() { public void serverGetFormattedElementOnInvocationContextIsNull() { when(routingContext.data()).thenReturn(null); when(routingContext.request()).thenReturn(serverRequest); - when(serverRequest.getHeader(Const.TRACE_ID_NAME)).thenReturn(null); + when(serverRequest.getHeader(CoreConst.TRACE_ID_NAME)).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), is("-")); } @@ -151,7 +151,7 @@ public void clientGetFormattedElementOnInvocationContextIsNull() { @Test public void serverGetFormattedElementOnDataIsNull() { - when(serverRequest.getHeader(Const.TRACE_ID_NAME)).thenReturn(null); + when(serverRequest.getHeader(CoreConst.TRACE_ID_NAME)).thenReturn(null); when(routingContext.request()).thenReturn(serverRequest); when(routingContext.data()).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathItemTest.java index 68b6af56973..d1601609a28 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathItemTest.java @@ -23,18 +23,18 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import io.swagger.models.Swagger; +import io.swagger.v3.oas.models.OpenAPI; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; @@ -58,9 +58,9 @@ public class UrlPathItemTest { private SchemaMeta schemaMeta; - private Swagger swagger; + private OpenAPI swagger; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; @@ -73,8 +73,8 @@ public void initStrBuilder() { serverRequest = Mockito.mock(HttpServerRequest.class); operationMeta = Mockito.mock(OperationMeta.class); schemaMeta = Mockito.mock(SchemaMeta.class); - swagger = Mockito.mock(Swagger.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + swagger = Mockito.mock(OpenAPI.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); accessLogEvent.setRoutingContext(routingContext); @@ -83,12 +83,11 @@ public void initStrBuilder() { @Test public void clientFormattedElement() { - String uri = "/base/test"; + String uri = "/test"; when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getOperationMeta()).thenReturn(operationMeta); when(invocation.getSchemaMeta()).thenReturn(schemaMeta); when(schemaMeta.getSwagger()).thenReturn(swagger); - when(swagger.getBasePath()).thenReturn("/base"); when(operationMeta.getOperationPath()).thenReturn("/test"); ITEM.appendClientFormattedItem(finishEvent, strBuilder); @@ -101,7 +100,7 @@ public void clientFormattedElement() { when(invocation.getSchemaMeta()).thenReturn(null); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.path()).thenReturn(uri); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals(uri, strBuilder.toString()); @@ -129,7 +128,7 @@ public void clientFormattedElementOnRequestIsNull() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(null); + when(restClientRequest.getHttpClientRequest()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @@ -148,7 +147,7 @@ public void clientFormattedElementOnMethodIsNull() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.path()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); diff --git a/common/common-access-log/src/test/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta b/common/common-access-log/src/test/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta index 501ac43ee58..74f9ee4cb24 100644 --- a/common/common-access-log/src/test/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta +++ b/common/common-access-log/src/test/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta @@ -16,4 +16,4 @@ # org.apache.servicecomb.common.accessLog.core.parser.impl.TestCompositeExtendedAccessLogItemMeta -org.apache.servicecomb.common.accessLog.core.parser.impl.TestSingleExtendedAccessLogItemMeta \ No newline at end of file +org.apache.servicecomb.common.accessLog.core.parser.impl.TestSingleExtendedAccessLogItemMeta diff --git a/common/common-protobuf/pom.xml b/common/common-protobuf/pom.xml index 08e2a9a0295..5b7ac5fcf7c 100644 --- a/common/common-protobuf/pom.xml +++ b/common/common-protobuf/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb common - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT common-protobuf Java Chassis::Common::Protobuf @@ -31,15 +31,17 @@ org.apache.servicecomb java-chassis-core - - org.apache.servicecomb - swagger-invocation-core - org.apache.servicecomb foundation-protobuf + + + com.google.protobuf + protobuf-java + test + org.apache.logging.log4j @@ -60,11 +62,6 @@ org.apache.servicecomb foundation-test-scaffolding - - javax.xml.bind - jaxb-api - test - @@ -82,7 +79,7 @@ - generate-sources + generate-test-sources test-compile diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/OperationProtobuf.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/OperationProtobuf.java index 2f0579fb760..6c12da844de 100644 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/OperationProtobuf.java +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/OperationProtobuf.java @@ -21,9 +21,6 @@ import java.util.Map; import java.util.Map.Entry; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.Response.Status.Family; - import org.apache.servicecomb.codec.protobuf.utils.ScopedProtobufSchemaManager; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; @@ -33,9 +30,10 @@ import com.fasterxml.jackson.databind.JavaType; import io.protostuff.compiler.model.Message; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status.Family; -@SuppressWarnings("rawtypes") -public class OperationProtobuf { +public final class OperationProtobuf { private RequestRootSerializer requestRootSerializer; private RequestRootDeserializer requestRootDeserializer; @@ -101,10 +99,10 @@ private void initConsumerRequestCodec(Invocation invocation, Message requestMess requestRootSerializer = new RequestRootSerializer( mapper.createRootSerializer(requestMessage, Object.class), true, false); } else { - if (invocation.getOperationMeta().getSwaggerOperation().getParameters().isEmpty()) { + if (invocation.getOperationMeta().parameterCount() == 0) { requestRootSerializer = new RequestRootSerializer(mapper.createRootSerializer(requestMessage, Object.class), false, false); - } else if (invocation.getOperationMeta().getSwaggerOperation().getParameters().size() == 1) { + } else if (invocation.getOperationMeta().parameterCount() == 1) { requestRootSerializer = new RequestRootSerializer(mapper.createRootSerializer(requestMessage, Object.class), false, true); } else { @@ -118,7 +116,7 @@ private void initRequestCodec(ScopedProtobufSchemaManager scopedProtobufSchemaMa ProtoMapper mapper = scopedProtobufSchemaManager.getOrCreateProtoMapper(invocation.getSchemaMeta()); Message requestMessage = mapper.getRequestMessage(invocation.getOperationMeta().getOperationId()); - if (!invocation.isConsumer()) { + if (invocation.isProducer()) { initProducerRequestCodec(invocation, requestMessage, mapper); } else { initConsumerRequestCodec(invocation, requestMessage, mapper); @@ -168,7 +166,7 @@ private void initResponseCodec(ScopedProtobufSchemaManager scopedProtobufSchemaM Message responseMessage = mapper.getResponseMessage(invocation.getOperationMeta().getOperationId()); JavaType responseType = invocation.findResponseType(Status.OK.getStatusCode()); - if (!invocation.isConsumer()) { + if (invocation.isProducer()) { initProviderResponseCode(responseMessage, mapper, responseType); } else { initConsumerResponseCode(responseMessage, mapper, responseType); diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ProtobufManager.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ProtobufManager.java index 07821788cb2..09ccb9d70cd 100644 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ProtobufManager.java +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ProtobufManager.java @@ -21,7 +21,7 @@ import java.util.Map; import java.util.Objects; -import javax.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.codec.protobuf.utils.ScopedProtobufSchemaManager; import org.apache.servicecomb.core.Invocation; diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootSerializer.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootSerializer.java index 263e2d786ea..ac5763d9b57 100644 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootSerializer.java +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootSerializer.java @@ -30,10 +30,10 @@ public class RequestRootSerializer { private final boolean isWrap; - public RequestRootSerializer(RootSerializer serializer, boolean isWrapp, boolean noTypesInfo) { + public RequestRootSerializer(RootSerializer serializer, boolean isWrap, boolean noTypesInfo) { this.rootSerializer = serializer; this.noTypesInfo = noTypesInfo; - this.isWrap = isWrapp; + this.isWrap = isWrap; } @SuppressWarnings("unchecked") diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootSerializer.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootSerializer.java index 07c2e1d6392..8af18a4a016 100644 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootSerializer.java +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootSerializer.java @@ -29,10 +29,10 @@ public class ResponseRootSerializer { private final boolean isWrap; - public ResponseRootSerializer(RootSerializer serializer, boolean isWrapp, boolean noTypesInfo) { + public ResponseRootSerializer(RootSerializer serializer, boolean isWrap, boolean noTypesInfo) { this.rootSerializer = serializer; this.noTypesInfo = noTypesInfo; - this.isWrap = isWrapp; + this.isWrap = isWrap; } public byte[] serialize(Object value) throws IOException { diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/BodyParameterAdapter.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/BodyParameterAdapter.java deleted file mode 100644 index 9dee93cc370..00000000000 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/BodyParameterAdapter.java +++ /dev/null @@ -1,102 +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.servicecomb.codec.protobuf.internal.converter; - -import java.util.Collections; -import java.util.List; - -import io.swagger.models.ArrayModel; -import io.swagger.models.Model; -import io.swagger.models.ModelImpl; -import io.swagger.models.RefModel; -import io.swagger.models.parameters.BodyParameter; -import io.swagger.models.properties.ObjectProperty; -import io.swagger.models.properties.Property; - -public class BodyParameterAdapter implements SwaggerTypeAdapter { - private final Model model; - - public BodyParameterAdapter(BodyParameter parameter) { - this.model = parameter.getSchema(); - } - - @Override - public String getRefType() { - if (model instanceof RefModel) { - return ((RefModel) model).getSimpleRef(); - } - - return null; - } - - @Override - public Property getArrayItem() { - if (model instanceof ArrayModel) { - return ((ArrayModel) model).getItems(); - } - - return null; - } - - @Override - public Property getMapItem() { - if (model instanceof ModelImpl) { - return ((ModelImpl) model).getAdditionalProperties(); - } - - return null; - } - - @Override - public List getEnum() { - if (model instanceof ModelImpl) { - return ((ModelImpl) model).getEnum(); - } - - return Collections.emptyList(); - } - - @Override - public String getType() { - if (model instanceof ModelImpl) { - return ((ModelImpl) model).getType(); - } - - return null; - } - - @Override - public String getFormat() { - if (model instanceof ModelImpl) { - return ((ModelImpl) model).getFormat(); - } - - return null; - } - - @Override - public boolean isJavaLangObject() { - if (model instanceof ModelImpl) { - ModelImpl modelImpl = (ModelImpl) model; - return (ObjectProperty.TYPE.equals(modelImpl.getType()) - && modelImpl.getProperties() == null - && modelImpl.getName() == null); - } - - return false; - } -} diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ModelAdapter.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ModelAdapter.java deleted file mode 100644 index 78dab6bc453..00000000000 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ModelAdapter.java +++ /dev/null @@ -1,102 +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.servicecomb.codec.protobuf.internal.converter; - -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -import io.swagger.models.ArrayModel; -import io.swagger.models.Model; -import io.swagger.models.ModelImpl; -import io.swagger.models.RefModel; -import io.swagger.models.properties.ObjectProperty; -import io.swagger.models.properties.Property; - -public class ModelAdapter implements SwaggerTypeAdapter { - private final Model model; - - public ModelAdapter(Model model) { - this.model = model; - } - - @Override - public String getRefType() { - if (model instanceof RefModel) { - return ((RefModel) model).getSimpleRef(); - } - - return null; - } - - @Override - public Property getArrayItem() { - if (model instanceof ArrayModel) { - return ((ArrayModel) model).getItems(); - } - - return null; - } - - @Override - public Property getMapItem() { - if (model instanceof ModelImpl) { - return ((ModelImpl) model).getAdditionalProperties(); - } - - return null; - } - - @Override - public List getEnum() { - if (model instanceof ModelImpl) { - return ((ModelImpl) model).getEnum(); - } - - return Collections.emptyList(); - } - - @Override - public String getType() { - if (model instanceof ModelImpl) { - return ((ModelImpl) model).getType(); - } - - return null; - } - - @Override - public String getFormat() { - if (model instanceof ModelImpl) { - return ((ModelImpl) model).getFormat(); - } - - return null; - } - - @Override - public boolean isJavaLangObject() { - if (model instanceof ModelImpl) { - ModelImpl modelImpl = (ModelImpl) model; - return Objects.equals(ObjectProperty.TYPE, modelImpl.getType()) - && modelImpl.getProperties() == null - && modelImpl.getName() == null; - } - - return false; - } -} diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/PropertyAdapter.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/PropertyAdapter.java deleted file mode 100644 index 4fc8ef15256..00000000000 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/PropertyAdapter.java +++ /dev/null @@ -1,86 +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.servicecomb.codec.protobuf.internal.converter; - -import java.util.Collections; -import java.util.List; - -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.ObjectProperty; -import io.swagger.models.properties.Property; -import io.swagger.models.properties.RefProperty; -import io.swagger.models.properties.StringProperty; - -public class PropertyAdapter implements SwaggerTypeAdapter { - private final Property property; - - public PropertyAdapter(Property property) { - this.property = property; - } - - @Override - public String getRefType() { - if (property instanceof RefProperty) { - return ((RefProperty) property).getSimpleRef(); - } - - return null; - } - - @Override - public Property getArrayItem() { - if (property instanceof ArrayProperty) { - return ((ArrayProperty) property).getItems(); - } - - return null; - } - - @Override - public Property getMapItem() { - if (property instanceof MapProperty) { - return ((MapProperty) property).getAdditionalProperties(); - } - - return null; - } - - @Override - public List getEnum() { - if (property instanceof StringProperty) { - return ((StringProperty) property).getEnum(); - } - - return Collections.emptyList(); - } - - @Override - public String getType() { - return property.getType(); - } - - @Override - public String getFormat() { - return property.getFormat(); - } - - @Override - public boolean isJavaLangObject() { - return property instanceof ObjectProperty; - } -} diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoMethod.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoMethod.java index e8d8a093c13..885dae823c9 100644 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoMethod.java +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoMethod.java @@ -18,19 +18,20 @@ import java.util.HashMap; import java.util.Map; - -import javax.ws.rs.core.Response.Status; +import java.util.Map.Entry; import org.apache.servicecomb.swagger.invocation.context.HttpStatus; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.ws.rs.core.Response.Status; + public class ProtoMethod { private String argTypeName; @JsonProperty // key is status - private Map responses = new HashMap<>(); + private final Map responses = new HashMap<>(); private ProtoResponse defaultResponse; @@ -62,6 +63,14 @@ public ProtoResponse findResponse(int statusCode) { return response; } + if (statusCode == Status.OK.getStatusCode()) { + for (Entry code : responses.entrySet()) { + if (HttpStatus.isSuccess(code.getKey())) { + return responses.get(code.getKey()); + } + } + } + if (HttpStatus.isSuccess(statusCode)) { return responses.get(Status.OK.getStatusCode()); } diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SchemaSwaggerTypeAdapter.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SchemaSwaggerTypeAdapter.java new file mode 100644 index 00000000000..68608e3db87 --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SchemaSwaggerTypeAdapter.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.servicecomb.codec.protobuf.internal.converter; + +import java.util.List; + +import io.swagger.v3.oas.models.media.Schema; + +public class SchemaSwaggerTypeAdapter implements SwaggerTypeAdapter { + private final Schema schema; + + public SchemaSwaggerTypeAdapter(Schema schema) { + this.schema = schema; + } + + @Override + public String getRefType() { + return schema.get$ref(); + } + + @Override + public Schema getArrayItem() { + return schema.getItems(); + } + + @Override + public Schema getMapItem() { + return (Schema) schema.getAdditionalProperties(); + } + + @Override + @SuppressWarnings("unchecked") + public List getEnum() { + return (List) schema.getEnum(); + } + + @Override + public String getType() { + return schema.getType(); + } + + @Override + public String getFormat() { + return schema.getFormat(); + } + + @Override + public boolean isJavaLangObject() { + return "object".equals(getType()); + } +} diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SerializableParameterAdapter.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SerializableParameterAdapter.java deleted file mode 100644 index fe2b8b81149..00000000000 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SerializableParameterAdapter.java +++ /dev/null @@ -1,69 +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.servicecomb.codec.protobuf.internal.converter; - -import java.util.List; - -import io.swagger.models.parameters.SerializableParameter; -import io.swagger.models.properties.Property; - -public class SerializableParameterAdapter implements SwaggerTypeAdapter { - private final SerializableParameter parameter; - - public SerializableParameterAdapter(SerializableParameter parameter) { - this.parameter = parameter; - } - - @Override - public String getRefType() { - return null; - } - - @Override - public Property getArrayItem() { - if ("array".equals(parameter.getType())) { - return parameter.getItems(); - } - - return null; - } - - @Override - public Property getMapItem() { - return null; - } - - @Override - public List getEnum() { - return parameter.getEnum(); - } - - @Override - public String getType() { - return parameter.getType(); - } - - @Override - public String getFormat() { - return parameter.getFormat(); - } - - @Override - public boolean isJavaLangObject() { - return false; - } -} diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SwaggerToProtoGenerator.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SwaggerToProtoGenerator.java index ec113ac0172..482808ab816 100644 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SwaggerToProtoGenerator.java +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SwaggerToProtoGenerator.java @@ -28,12 +28,10 @@ import java.util.Map.Entry; import java.util.Set; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response.Status; - import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; import org.apache.servicecomb.foundation.protobuf.internal.parser.ProtoParser; +import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,22 +39,25 @@ import io.protostuff.compiler.model.Message; import io.protostuff.compiler.model.Proto; -import io.swagger.models.Model; -import io.swagger.models.ModelImpl; -import io.swagger.models.Operation; -import io.swagger.models.Path; -import io.swagger.models.Response; -import io.swagger.models.Swagger; -import io.swagger.models.parameters.Parameter; -import io.swagger.models.properties.Property; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.responses.ApiResponse; import io.vertx.core.json.Json; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response.Status; +@SuppressWarnings({"rawtypes", "unchecked"}) public class SwaggerToProtoGenerator { private static final Logger LOGGER = LoggerFactory.getLogger(SwaggerToProtoGenerator.class); private final String protoPackage; - private final Swagger swagger; + private final OpenAPI swagger; private final StringBuilder msgStringBuilder = new StringBuilder(); @@ -70,7 +71,7 @@ public class SwaggerToProtoGenerator { // not java package // better to be: app_${app}.mid_{microservice}.sid_{schemaId} - public SwaggerToProtoGenerator(String protoPackage, Swagger swagger) { + public SwaggerToProtoGenerator(String protoPackage, OpenAPI swagger) { this.protoPackage = escapePackageName(protoPackage); this.swagger = swagger; } @@ -78,22 +79,19 @@ public SwaggerToProtoGenerator(String protoPackage, Swagger swagger) { public Proto convert() { convertDefinitions(); convertOperations(); - for (; ; ) { + do { List oldPending = pending; pending = new ArrayList<>(); for (Runnable runnable : oldPending) { runnable.run(); } - if (pending.isEmpty()) { - break; - } - } + } while (!pending.isEmpty()); return createProto(); } public static String escapePackageName(String name) { - return name.replaceAll("[\\-\\:]", "_"); + return name.replaceAll("[\\-:]", "_"); } public static String escapeMessageName(String name) { @@ -101,34 +99,31 @@ public static String escapeMessageName(String name) { } public static boolean isValidEnum(String name) { - if (name.contains(".") || name.contains("-")) { - return false; - } - return true; + return !name.contains(".") && !name.contains("-"); } private void convertDefinitions() { - if (swagger.getDefinitions() == null) { + if (swagger.getComponents() == null || swagger.getComponents().getSchemas() == null) { return; } - for (Entry entry : swagger.getDefinitions().entrySet()) { - convertDefinition(entry.getKey(), (ModelImpl) entry.getValue()); + for (Entry entry : swagger.getComponents().getSchemas().entrySet()) { + convertDefinition(entry.getKey(), entry.getValue()); } } @SuppressWarnings("unchecked") - private void convertDefinition(String modelName, ModelImpl model) { - Map properties = model.getProperties(); + private void convertDefinition(String modelName, Schema model) { + Map properties = model.getProperties(); if (properties == null) { // it's a empty message properties = Collections.emptyMap(); } - createMessage(modelName, (Map) (Object) properties); + createMessage(modelName, properties); } - private void createMessage(String protoName, Map properties, String... annotations) { + private void createMessage(String protoName, Map properties, String... annotations) { if (!messages.add(protoName)) { // already created return; @@ -140,8 +135,8 @@ private void createMessage(String protoName, Map properties, Str } appendLine(msgStringBuilder, "message %s {", protoName); int tag = 1; - for (Entry entry : properties.entrySet()) { - Object property = entry.getValue(); + for (Entry entry : properties.entrySet()) { + Schema property = entry.getValue(); String propertyType = convertSwaggerType(property); appendLine(msgStringBuilder, " %s %s = %d;", propertyType, entry.getKey(), tag); @@ -177,10 +172,10 @@ private String convertSwaggerType(Object swaggerType) { type = adapter.getRefType(); if (type != null) { - return type; + return type.substring(Components.COMPONENTS_SCHEMAS_REF.length()); } - Property itemProperty = adapter.getArrayItem(); + Schema itemProperty = adapter.getArrayItem(); if (itemProperty != null) { return "repeated " + convertArrayOrMapItem(itemProperty); } @@ -200,7 +195,7 @@ private String convertSwaggerType(Object swaggerType) { Json.encode(swaggerType))); } - private String convertArrayOrMapItem(Property itemProperty) { + private String convertArrayOrMapItem(Schema itemProperty) { SwaggerTypeAdapter itemAdapter = SwaggerTypeAdapter.create(itemProperty); // List>, need to wrap if (itemAdapter.getArrayItem() != null) { @@ -219,7 +214,7 @@ private String convertArrayOrMapItem(Property itemProperty) { return convertSwaggerType(itemProperty); } - private String generateWrapPropertyName(String prefix, Property property) { + private String generateWrapPropertyName(String prefix, Schema property) { SwaggerTypeAdapter adapter = SwaggerTypeAdapter.create(property); // List>, need to wrap if (adapter.getArrayItem() != null) { @@ -235,7 +230,7 @@ private String generateWrapPropertyName(String prefix, Property property) { return prefix + StringUtils.capitalize(escapeMessageName(convertSwaggerType(adapter))); } - private void wrapPropertyToMessage(String protoName, Object property) { + private void wrapPropertyToMessage(String protoName, Schema property) { createMessage(protoName, Collections.singletonMap("value", property), ProtoConst.ANNOTATION_WRAP_PROPERTY); } @@ -269,47 +264,32 @@ private void createEnum(String enumName, List enums) { private String findBaseType(String swaggerType, String swaggerFmt) { String key = swaggerType + ":" + swaggerFmt; - switch (key) { - case "boolean:null": - return "bool"; - // there is no int8/int16 in protobuf - case "integer:null": - return "int64"; - case "integer:int8": - case "integer:int16": - case "integer:int32": - return "int32"; - case "integer:int64": - return "int64"; - case "number:null": - return "double"; - case "number:float": - return "float"; - case "number:double": - return "double"; - case "string:null": - return "string"; - case "string:byte": - return "bytes"; - case "string:date": // LocalDate - case "string:date-time": // Date - return "int64"; - case "file:null": - throw new IllegalStateException("not support swagger type: " + swaggerType); - default: - return null; - } + return switch (key) { + case "boolean:null" -> "bool"; + case "integer:int32" -> "sint32"; + case "integer:int64" -> "sint64"; + case "integer:null" -> "string"; // BigInteger like values + case "number:double" -> "double"; + case "number:float" -> "float"; + case "number:null" -> "string"; // BigDecimal like values + case "string:null" -> "string"; + case "string:byte" -> "bytes"; + case "string:date" -> "int64"; + case "string:date-time" -> "int64"; + case "string:binary" -> throw new IllegalArgumentException("proto buffer not support file upload/download"); + default -> null; + }; } private void convertOperations() { - Map paths = swagger.getPaths(); + Paths paths = swagger.getPaths(); if (paths == null || paths.isEmpty()) { return; } appendLine(serviceBuilder, "service MainService {"); - for (Path path : paths.values()) { - for (Operation operation : path.getOperationMap().values()) { + for (PathItem path : paths.values()) { + for (Operation operation : path.readOperations()) { if (isUpload(operation)) { LOGGER.warn("Not support operation for highway {}.{}, {}", this.protoPackage, operation.getOperationId(), "file upload not supported"); @@ -333,17 +313,15 @@ private void convertOperations() { } private boolean isUpload(Operation operation) { - return operation.getConsumes() != null && operation.getConsumes().contains(MediaType.MULTIPART_FORM_DATA); + return operation.getRequestBody() != null && operation.getRequestBody().getContent() != null + && operation.getRequestBody().getContent().get(MediaType.MULTIPART_FORM_DATA) != null; } private boolean isDownload(Operation operation) { - if (operation.getResponses().get("200").getResponseSchema() instanceof ModelImpl) { - ModelImpl model = (ModelImpl) operation.getResponses().get("200").getResponseSchema(); - if ("file".equals(model.getType())) { - return true; - } - } - return false; + return operation.getResponses().get(SwaggerConst.SUCCESS_KEY) != null && + operation.getResponses().get(SwaggerConst.SUCCESS_KEY).getContent() != null && + operation.getResponses().get(SwaggerConst.SUCCESS_KEY).getContent().get(MediaType.MULTIPART_FORM_DATA) + != null; } private void convertOperation(Operation operation) { @@ -358,15 +336,15 @@ private void convertOperation(Operation operation) { } private void fillRequestType(Operation operation, ProtoMethod protoMethod) { - List parameters = operation.getParameters(); - if (parameters.isEmpty()) { + int parametersCount = parametersCount(operation); + if (parametersCount == 0) { addImports(ProtoConst.EMPTY_PROTO); protoMethod.setArgTypeName(ProtoConst.EMPTY.getCanonicalName()); return; } - if (parameters.size() == 1) { - String type = convertSwaggerType(parameters.get(0)); + if (parametersCount == 1) { + String type = convertSwaggerType(oneSchema(operation)); if (messages.contains(type)) { protoMethod.setArgTypeName(type); return; @@ -374,14 +352,67 @@ private void fillRequestType(Operation operation, ProtoMethod protoMethod) { } String wrapName = StringUtils.capitalize(operation.getOperationId()) + "RequestWrap"; - createWrapArgs(wrapName, parameters); + createWrapArgs(wrapName, wrapSchema(operation)); protoMethod.setArgTypeName(wrapName); } + private Map wrapSchema(Operation operation) { + Map properties = new LinkedHashMap<>(); + if (operation.getParameters() != null) { + for (Parameter parameter : operation.getParameters()) { + properties.put(parameter.getName(), parameter.getSchema()); + } + } + if (operation.getRequestBody() != null + && operation.getRequestBody().getContent().size() != 0) { + if (operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null) { + operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties() + .forEach((k, v) -> properties.put((String) k, (Schema) v)); + } else { + properties.put((String) operation.getRequestBody().getExtensions().get(SwaggerConst.EXT_BODY_NAME), + operation.getRequestBody().getContent().get( + operation.getRequestBody().getContent().keySet().iterator().next()).getSchema()); + } + } + return properties; + } + + private Schema oneSchema(Operation operation) { + if (operation.getParameters() != null && operation.getParameters().size() == 1) { + return operation.getParameters().get(0).getSchema(); + } + if (operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null) { + return (Schema) operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema() + .getProperties() + .values().iterator().next(); + } + return operation.getRequestBody().getContent().get( + operation.getRequestBody().getContent().keySet().iterator().next()).getSchema(); + } + + private int parametersCount(Operation operation) { + int parameters = operation.getParameters() == null ? 0 : operation.getParameters().size(); + if (operation.getRequestBody() != null) { + if (operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null) { + parameters = parameters + operation.getRequestBody() + .getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties().size(); + } else if (operation.getRequestBody().getContent().size() != 0) { + parameters = parameters + 1; + } + } + return parameters; + } + private void fillResponseType(Operation operation, ProtoMethod protoMethod) { - for (Entry entry : operation.getResponses().entrySet()) { - String type = convertSwaggerType(entry.getValue().getResponseSchema()); + for (Entry entry : operation.getResponses().entrySet()) { + Schema schema = null; + if (entry.getValue().getContent() != null && + entry.getValue().getContent().size() != 0) { + schema = entry.getValue().getContent().get( + entry.getValue().getContent().keySet().iterator().next()).getSchema(); + } + String type = convertSwaggerType(schema); boolean wrapped = !messages.contains(type); ProtoResponse protoResponse = new ProtoResponse(); @@ -389,7 +420,7 @@ private void fillResponseType(Operation operation, ProtoMethod protoMethod) { if (wrapped) { String wrapName = StringUtils.capitalize(operation.getOperationId()) + "ResponseWrap" + entry.getKey(); - wrapPropertyToMessage(wrapName, entry.getValue().getResponseSchema()); + wrapPropertyToMessage(wrapName, schema); protoResponse.setTypeName(wrapName); } @@ -397,11 +428,7 @@ private void fillResponseType(Operation operation, ProtoMethod protoMethod) { } } - private void createWrapArgs(String wrapName, List parameters) { - Map properties = new LinkedHashMap<>(); - for (Parameter parameter : parameters) { - properties.put(parameter.getName(), parameter); - } + private void createWrapArgs(String wrapName, Map properties) { createMessage(wrapName, properties, ProtoConst.ANNOTATION_WRAP_ARGUMENTS); } diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SwaggerTypeAdapter.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SwaggerTypeAdapter.java index 2fd0580ec9c..80a20fd2822 100644 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SwaggerTypeAdapter.java +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SwaggerTypeAdapter.java @@ -18,10 +18,7 @@ import java.util.List; -import io.swagger.models.Model; -import io.swagger.models.parameters.BodyParameter; -import io.swagger.models.parameters.SerializableParameter; -import io.swagger.models.properties.Property; +import io.swagger.v3.oas.models.media.Schema; public interface SwaggerTypeAdapter { static SwaggerTypeAdapter create(Object swaggerType) { @@ -29,20 +26,8 @@ static SwaggerTypeAdapter create(Object swaggerType) { return (SwaggerTypeAdapter) swaggerType; } - if (swaggerType instanceof Model) { - return new ModelAdapter((Model) swaggerType); - } - - if (swaggerType instanceof Property) { - return new PropertyAdapter((Property) swaggerType); - } - - if (swaggerType instanceof SerializableParameter) { - return new SerializableParameterAdapter((SerializableParameter) swaggerType); - } - - if (swaggerType instanceof BodyParameter) { - return new BodyParameterAdapter((BodyParameter) swaggerType); + if (swaggerType instanceof Schema) { + return new SchemaSwaggerTypeAdapter((Schema) swaggerType); } throw new IllegalStateException("not support swagger type: " + swaggerType.getClass().getName()); @@ -50,9 +35,9 @@ static SwaggerTypeAdapter create(Object swaggerType) { String getRefType(); - Property getArrayItem(); + Schema getArrayItem(); - Property getMapItem(); + Schema getMapItem(); List getEnum(); diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/schema/SchemaToProtoGenerator.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/schema/SchemaToProtoGenerator.java new file mode 100644 index 00000000000..0220cd96b30 --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/schema/SchemaToProtoGenerator.java @@ -0,0 +1,416 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.codec.protobuf.schema; + + +import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; +import org.apache.servicecomb.foundation.protobuf.internal.parser.ProtoParser; +import org.apache.servicecomb.swagger.SwaggerUtils; + +import com.google.common.hash.Hashing; + +import io.protostuff.compiler.model.Proto; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class SchemaToProtoGenerator { + record IdentifierRunnable(Schema identifier, Runnable target) + implements Runnable { + + @Override + public void run() { + this.target.run(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + IdentifierRunnable that = (IdentifierRunnable) o; + return SwaggerUtils.schemaEquals(identifier, that.identifier); + } + + @Override + public int hashCode() { + return SwaggerUtils.schemaHashCode(identifier); + } + } + + private final String protoPackage; + + private final OpenAPI openAPI; + + private final Schema rootSchema; + + private final String rootName; + + private final Set messages = new HashSet<>(); + + private final StringBuilder msgStringBuilder = new StringBuilder(); + + private List pending = new ArrayList<>(); + + public SchemaToProtoGenerator(String protoPackage, OpenAPI openAPI, Schema rootSchema, String rootName) { + this.protoPackage = protoPackage; + this.openAPI = openAPI; + this.rootSchema = rootSchema; + this.rootName = rootName; + } + + public Proto convert() { + createMessage(this.rootSchema); + + int iteration = 0; + do { + List oldPending = pending; + pending = new ArrayList<>(); + for (Runnable runnable : oldPending) { + runnable.run(); + } + if (pending.size() >= oldPending.size()) { + iteration++; + } + } while (!pending.isEmpty() && iteration < 1000); + + if (iteration == 1000) { + throw new IllegalArgumentException( + String.format("Failed to create schema %s. May be cyclic object.", this.rootName)); + } + + Map wrap = new HashMap<>(1); + wrap.put("value", this.rootSchema); + createMessage(rootName, wrap, ProtoConst.ANNOTATION_WRAP_PROPERTY); + + return createProto(); + } + + protected Proto createProto() { + StringBuilder sb = new StringBuilder(); + appendLine(sb, "syntax = \"proto3\";"); + if (StringUtils.isNotEmpty(protoPackage)) { + sb.append("package ").append(protoPackage).append(";\n"); + } + sb.append(msgStringBuilder); + ProtoParser protoParser = new ProtoParser(); + return protoParser.parseFromContent(sb.toString()); + } + + private String findSchemaType(Schema schema) { + @SuppressWarnings("unchecked") + String type = tryFindEnumType((List) schema.getEnum()); + if (type != null) { + return type; + } + + type = findBaseType(schema.getType(), schema.getFormat()); + if (type != null) { + return type; + } + + Schema itemProperty = schema.getItems(); + if (itemProperty != null) { + String containerType = findArrayOrMapItemType(itemProperty); + if (containerType != null) { + return "repeated " + containerType; + } + return null; + } + + itemProperty = (Schema) schema.getAdditionalProperties(); + if (itemProperty != null) { + String containerType = findArrayOrMapItemType(itemProperty); + if (containerType != null) { + return String.format("map", containerType); + } + return null; + } + + type = schema.get$ref(); + if (type != null) { + String typeName = type.substring(Components.COMPONENTS_SCHEMAS_REF.length()); + Schema refSchema = openAPI.getComponents().getSchemas().get(typeName); + if (refSchema == null) { + throw new IllegalArgumentException("not found ref in components " + type); + } + if (StringUtils.isEmpty(refSchema.getName())) { + refSchema.setName(typeName); + } + return findSchemaType(refSchema); + } + + return findObjectType(schema); + } + + private String findObjectType(Schema schema) { + String name = schema.getName(); + if (messages.contains(name)) { + return name; + } + return null; + } + + private void createEnum(String enumName, List enums) { + if (!messages.add(enumName)) { + // already created + return; + } + + appendLine(msgStringBuilder, "enum %s {", enumName); + for (int idx = 0; idx < enums.size(); idx++) { + if (isValidEnum(enums.get(idx))) { + appendLine(msgStringBuilder, " %s =%d;", enums.get(idx), idx); + } else { + throw new IllegalStateException( + String.format("enum class [%s] name [%s] not supported by protobuffer.", enumName, enums.get(idx))); + } + } + appendLine(msgStringBuilder, "}"); + } + + public static boolean isValidEnum(String name) { + return !name.contains(".") && !name.contains("-"); + } + + private String tryFindEnumType(List enums) { + if (enums != null && !enums.isEmpty()) { + String strEnums = enums.toString(); + String enumName = "Enum_" + Hashing.sha256().hashString(strEnums, StandardCharsets.UTF_8); + pending.add(() -> createEnum(enumName, enums)); + return enumName; + } + return null; + } + + private String findBaseType(String swaggerType, String swaggerFmt) { + String key = swaggerType + ":" + swaggerFmt; + return switch (key) { + case "boolean:null" -> "bool"; + case "integer:int32" -> "sint32"; + case "integer:int64" -> "sint64"; + case "integer:null" -> "string"; // BigInteger like values + case "number:double" -> "double"; + case "number:float" -> "float"; + case "number:null" -> "string"; // BigDecimal like values + case "string:null" -> "string"; + case "string:byte" -> "bytes"; + case "string:date" -> "int64"; + case "string:date-time" -> "int64"; + case "string:binary" -> throw new IllegalArgumentException("proto buffer not support file upload/download"); + default -> null; + }; + } + + private String findArrayOrMapItemType(Schema itemProperty) { + // List>, need to wrap + if (itemProperty.getItems() != null) { + return findWrapPropertyType(List.class.getSimpleName(), itemProperty.getItems()); + } + + // List>, need to wrap + if (itemProperty.getAdditionalProperties() != null) { + return findWrapPropertyType(Map.class.getSimpleName(), (Schema) itemProperty.getAdditionalProperties()); + } + + return findSchemaType(itemProperty); + } + + + private String findWrapPropertyType(String prefix, Schema property) { + // List>, need to wrap + if (property.getItems() != null) { + return findWrapPropertyType(prefix + List.class.getSimpleName(), property.getItems()); + } + + // List>, need to wrap + if (property.getAdditionalProperties() != null) { + return findWrapPropertyType(prefix + Map.class.getSimpleName(), + (Schema) property.getAdditionalProperties()); + } + + String type = findSchemaType(property); + if (type == null) { + return null; + } + + // message name cannot have . (package separator) + return prefix + StringUtils.capitalize(escapeMessageName(type)); + } + + public static String escapeMessageName(String name) { + return name.replaceAll("\\.", "_"); + } + + + private void wrapPropertyToMessage(String protoName, Schema property) { + createMessage(protoName, Collections.singletonMap("value", property), ProtoConst.ANNOTATION_WRAP_PROPERTY); + } + + private void createMessage(String protoName, Map properties, String... annotations) { + for (String annotation : annotations) { + msgStringBuilder.append("//"); + appendLine(msgStringBuilder, annotation); + } + appendLine(msgStringBuilder, "message %s {", protoName); + int tag = 1; + for (Entry entry : properties.entrySet()) { + Schema property = entry.getValue(); + String propertyType = findSchemaType(property); + + appendLine(msgStringBuilder, " %s %s = %d;", propertyType, entry.getKey(), tag); + tag++; + } + appendLine(msgStringBuilder, "}"); + } + + public void createMessage(Schema schema) { + String ref = schema.get$ref(); + if (ref != null) { + String typeName = ref.substring(Components.COMPONENTS_SCHEMAS_REF.length()); + Schema refSchema = openAPI.getComponents().getSchemas().get(typeName); + if (refSchema == null) { + throw new IllegalArgumentException("not found ref in components " + ref); + } + if (StringUtils.isEmpty(refSchema.getName())) { + refSchema.setName(typeName); + } + createMessage(refSchema); + return; + } + + boolean wait = false; + + //array or map + if (isArrayOrMap(schema)) { + Schema mapOrArrayItem = arrayOrMapItem(schema); + if (findSchemaType(mapOrArrayItem) == null) { + createMessageTask(mapOrArrayItem); + wait = true; + } else { + if (createMapOrArrayMessageTask(mapOrArrayItem, true, schema)) { + wait = true; + } + } + } + + //object + if (schema.getProperties() != null) { + for (Entry entry : schema.getProperties().entrySet()) { + if (findSchemaType(entry.getValue()) == null) { + createMessageTask(entry.getValue()); + wait = true; + } else if (isArrayOrMap(entry.getValue())) { + if (createMapOrArrayMessageTask(arrayOrMapItem(entry.getValue()), false, null)) { + wait = true; + } + } + } + } + + if (wait) { + IdentifierRunnable runnable = new IdentifierRunnable(schema, () -> createMessage(schema)); + if (!pending.contains(runnable)) { + pending.add(runnable); + } + return; + } + + if (findSchemaType(schema) != null) { + return; + } + + messages.add(schema.getName()); + + appendLine(msgStringBuilder, "message %s {", schema.getName()); + List> sortedProperties = new ArrayList<>(schema.getProperties().entrySet()); + sortedProperties.sort(Comparator.comparing(Map.Entry::getKey)); + int tag = 1; + for (Entry entry : sortedProperties) { + Schema property = entry.getValue(); + String propertyType = findSchemaType(property); + + appendLine(msgStringBuilder, " %s %s = %d;", propertyType, entry.getKey(), tag); + tag++; + } + appendLine(msgStringBuilder, "}"); + } + + private boolean isArrayOrMap(Schema value) { + return value.getItems() != null || value.getAdditionalProperties() != null; + } + + private Schema arrayOrMapItem(Schema schema) { + return schema.getItems() == null ? + (Schema) schema.getAdditionalProperties() : schema.getItems(); + } + + private void createMessageTask(Schema schema) { + IdentifierRunnable runnable = new IdentifierRunnable(schema, () -> createMessage(schema)); + if (!pending.contains(runnable)) { + pending.add(runnable); + } + } + + private boolean createMapOrArrayMessageTask(Schema schema, boolean nested, Schema owner) { + if (schema.getAdditionalProperties() != null) { + String protoName = findWrapPropertyType(Map.class.getSimpleName(), + (Schema) schema.getAdditionalProperties()); + if (messages.add(protoName)) { + pending.add(() -> wrapPropertyToMessage(protoName, schema)); + createMessageTask((Schema) schema.getAdditionalProperties()); + return true; + } + } + if (schema.getItems() != null) { + String protoName = findWrapPropertyType(List.class.getSimpleName(), schema.getItems()); + if (messages.add(protoName)) { + pending.add(() -> wrapPropertyToMessage(protoName, schema)); + createMessageTask(schema.getItems()); + return true; + } + } + if (nested) { + String protoName = owner.getAdditionalProperties() != null ? + findWrapPropertyType(Map.class.getSimpleName(), schema) : + findWrapPropertyType(List.class.getSimpleName(), schema); + if (messages.add(protoName)) { + pending.add(() -> wrapPropertyToMessage(protoName, owner)); + return true; + } + } + return false; + } +} diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java index 632787699bc..3c7e5adbb64 100644 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java @@ -19,14 +19,18 @@ import java.util.Map; +import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.codec.protobuf.internal.converter.SwaggerToProtoGenerator; +import org.apache.servicecomb.codec.protobuf.schema.SchemaToProtoGenerator; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; +import org.apache.servicecomb.swagger.SwaggerUtils; import io.protostuff.compiler.model.Proto; -import io.swagger.models.Swagger; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; /** * Manage swagger -> protoBuffer mappings. @@ -35,9 +39,49 @@ * for each MicroserviceMeta. */ public class ScopedProtobufSchemaManager { + static class SchemaKey { + String schemaId; + + String rootMessage; + + Schema schema; + + int hashCode = -1; + + SchemaKey(String schemaId, String rootMessage, Schema schema) { + this.schemaId = schemaId; + this.rootMessage = rootMessage; + this.schema = schema; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SchemaKey other = (SchemaKey) o; + return StringUtils.equals(schemaId, other.schemaId) && StringUtils.equals(rootMessage, other.rootMessage) + && SwaggerUtils.schemaEquals(schema, other.schema); + } + + @Override + public int hashCode() { + if (hashCode != -1) { + return hashCode; + } + hashCode = schemaId.hashCode() ^ rootMessage.hashCode() ^ SwaggerUtils.schemaHashCode(schema); + return hashCode; + } + } + // Because this class belongs to each SchemaMeta, the key is the schema id. private final Map mapperCache = new ConcurrentHashMapEx<>(); + private final Map schemaMapperCache = new ConcurrentHashMapEx<>(); + public ScopedProtobufSchemaManager() { } @@ -47,7 +91,7 @@ public ScopedProtobufSchemaManager() { */ public ProtoMapper getOrCreateProtoMapper(SchemaMeta schemaMeta) { return mapperCache.computeIfAbsent(schemaMeta.getSchemaId(), key -> { - Swagger swagger = schemaMeta.getSwagger(); + OpenAPI swagger = schemaMeta.getSwagger(); SwaggerToProtoGenerator generator = new SwaggerToProtoGenerator(schemaMeta.getMicroserviceQualifiedName(), swagger); Proto proto = generator.convert(); @@ -55,4 +99,19 @@ public ProtoMapper getOrCreateProtoMapper(SchemaMeta schemaMeta) { return protoMapperFactory.create(proto); }); } + + /** + * get the ProtoMapper from Schema + */ + public ProtoMapper getOrCreateProtoMapper(OpenAPI openAPI, String schemaId, String rootMessageName, + Schema schema) { + SchemaKey schemaKey = new SchemaKey(schemaId, rootMessageName, schema); + return schemaMapperCache.computeIfAbsent(schemaKey, key -> { + SchemaToProtoGenerator generator = new SchemaToProtoGenerator("scb.schema", openAPI, + key.schema, rootMessageName); + Proto proto = generator.convert(); + ProtoMapperFactory protoMapperFactory = new ProtoMapperFactory(); + return protoMapperFactory.create(proto); + }); + } } diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodec.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodec.java index aa66b9f2730..141b7cf4b2c 100644 --- a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodec.java +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodec.java @@ -42,7 +42,6 @@ import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.executor.ExecutorManager; -import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.apache.servicecomb.foundation.test.scaffolding.model.Empty; import org.apache.servicecomb.foundation.test.scaffolding.model.People; @@ -61,8 +60,6 @@ import com.fasterxml.jackson.databind.type.TypeFactory; -import io.swagger.models.Swagger; - /** * SchemaMetaCodec test cases. This test cases covers POJO invoker and producer. */ @@ -96,17 +93,15 @@ private void mockSchemaMeta(String schemaId, AbstractSwaggerGenerator swaggerGen Mockito.when(consumerMicroserviceMeta.getMicroserviceVersionsMeta()).thenReturn(microserviceVersionsMeta); Mockito.when(consumerMicroserviceMeta.getMicroserviceName()).thenReturn("test"); Mockito.when(consumerMicroserviceMeta.getExtData(ProtobufManager.EXT_ID)).thenReturn(null); - Swagger swagger = swaggerGenerator.generate(); SwaggerEnvironment swaggerEnvironment = new SwaggerEnvironment(); - - providerSchemaMeta = new SchemaMeta(providerMicroserviceMeta, schemaId, swagger); - SwaggerProducer swaggerProducer = swaggerEnvironment.createProducer(producerInstance, swagger); + SwaggerProducer swaggerProducer = swaggerEnvironment.createProducer(producerInstance); + providerSchemaMeta = new SchemaMeta(providerMicroserviceMeta, schemaId, swaggerProducer.getSwagger()); for (SwaggerProducerOperation producerOperation : swaggerProducer.getAllOperations()) { OperationMeta operationMeta = providerSchemaMeta.ensureFindOperation(producerOperation.getOperationId()); operationMeta.setSwaggerProducerOperation(producerOperation); } - consumerSchemaMeta = new SchemaMeta(consumerMicroserviceMeta, schemaId, swagger); + consumerSchemaMeta = new SchemaMeta(consumerMicroserviceMeta, schemaId, swaggerProducer.getSwagger()); } @Test @@ -123,18 +118,18 @@ public void testProtoSchemaOperationUserPOJO() throws Exception { private Invocation mockInvocation(String operation, InvocationType invocationType) { OperationMeta operationMeta; - boolean isConsumer; + boolean isProvider; Invocation invocation = Mockito.mock(Invocation.class); InvocationRuntimeType invocationRuntimeType; if (InvocationType.CONSUMER == invocationType) { operationMeta = consumerSchemaMeta.getOperations().get(operation); - isConsumer = true; + isProvider = false; Mockito.when(invocation.getSchemaMeta()).thenReturn(consumerSchemaMeta); invocationRuntimeType = operationMeta.buildBaseConsumerRuntimeType(); } else { operationMeta = providerSchemaMeta.getOperations().get(operation); - isConsumer = false; + isProvider = true; Mockito.when(invocation.getSchemaMeta()).thenReturn(providerSchemaMeta); invocationRuntimeType = operationMeta.buildBaseProviderRuntimeType(); } @@ -148,13 +143,13 @@ private Invocation mockInvocation(String operation, InvocationType invocationTyp Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); Mockito.when(invocation.getMicroserviceMeta()).thenReturn(microserviceMeta); - Mockito.when(invocation.isConsumer()).thenReturn(isConsumer); + Mockito.when(invocation.isProducer()).thenReturn(isProvider); return invocation; } private void testProtoSchemaOperationUserImpl() throws IOException { Invocation consumerInvocation = mockInvocation("user", InvocationType.CONSUMER); - Invocation providerInvocation = mockInvocation("user", InvocationType.PRODUCER); + Invocation providerInvocation = mockInvocation("user", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); @@ -229,7 +224,7 @@ public void testProtoSchemaOperationmapUserPOJO() throws Exception { private void testProtoSchemaOperationmapUserImpl(boolean isPojo) throws IOException { Invocation consumerInvocation = mockInvocation("mapUser", InvocationType.CONSUMER); - Invocation providerInvocation = mockInvocation("mapUser", InvocationType.PRODUCER); + Invocation providerInvocation = mockInvocation("mapUser", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); @@ -253,7 +248,7 @@ private void testProtoSchemaOperationmapUserImpl(boolean isPojo) throws IOExcept args.put("users", userMap); if (isPojo) { Map swaggerArgs = new HashMap<>(1); - swaggerArgs.put("users", args); + swaggerArgs.put("listListUserBody", args); values = requestSerializer.serialize(swaggerArgs); } else { values = requestSerializer.serialize(args); @@ -261,7 +256,7 @@ private void testProtoSchemaOperationmapUserImpl(boolean isPojo) throws IOExcept RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); Map decodedUserArgs = requestDeserializer.deserialize(values); if (isPojo) { - decodedUserArgs = (Map) decodedUserArgs.get("users"); + decodedUserArgs = (Map) decodedUserArgs.get("mapUserBody"); Assertions.assertEquals(user.name, ((Map>) decodedUserArgs.get("users")).get("test").get("name")); Assertions.assertEquals(user.friends.get(0).name, @@ -276,13 +271,15 @@ private void testProtoSchemaOperationmapUserImpl(boolean isPojo) throws IOExcept ResponseRootSerializer responseSerializer = providerOperationProtobuf.findResponseRootSerializer(200); values = responseSerializer.serialize(userMap); ResponseRootDeserializer responseDeserializer = consumerOperationProtobuf.findResponseRootDeserializer(200); - Map decodedUser = (Map) responseDeserializer.deserialize(values, ProtoConst.MAP_TYPE); + Map decodedUser = (Map) responseDeserializer.deserialize(values, + TypeFactory.defaultInstance().constructMapType(HashMap.class, String.class, User.class)); Assertions.assertEquals(user.name, decodedUser.get("test").name); Assertions.assertEquals(user.friends.get(0).name, decodedUser.get("test").friends.get(0).name); user.friends = new ArrayList<>(); values = responseSerializer.serialize(userMap); - decodedUser = (Map) responseDeserializer.deserialize(values, ProtoConst.MAP_TYPE); + decodedUser = (Map) responseDeserializer.deserialize(values, + TypeFactory.defaultInstance().constructMapType(HashMap.class, String.class, User.class)); Assertions.assertEquals(user.name, decodedUser.get("test").name); // proto buffer encode and decode empty list to be null Assertions.assertNull(decodedUser.get("test").friends); @@ -302,7 +299,7 @@ public void testProtoSchemaOperationBasePOJO() throws Exception { private void testProtoSchemaOperationBaseImpl(boolean isPojo) throws IOException { Invocation consumerInvocation = mockInvocation("base", InvocationType.CONSUMER); - Invocation providerInvocation = mockInvocation("base", InvocationType.PRODUCER); + Invocation providerInvocation = mockInvocation("base", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); @@ -421,7 +418,7 @@ public void testProtoSchemaOperationlistListUserPOJO() throws Exception { private void testProtoSchemaOperationlistListUserImpl(boolean isPojo) throws IOException { Invocation consumerInvocation = mockInvocation("listListUser", InvocationType.CONSUMER); - Invocation providerInvocation = mockInvocation("listListUser", InvocationType.PRODUCER); + Invocation providerInvocation = mockInvocation("listListUser", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); @@ -447,7 +444,7 @@ private void testProtoSchemaOperationlistListUserImpl(boolean isPojo) throws IOE if (isPojo) { Map swaggerArgs = new HashMap<>(); - swaggerArgs.put("value", args); + swaggerArgs.put("listListUserBody", args); values = requestSerializer.serialize(swaggerArgs); } else { values = requestSerializer.serialize(args); @@ -457,7 +454,7 @@ private void testProtoSchemaOperationlistListUserImpl(boolean isPojo) throws IOE Map decodedArgs; if (isPojo) { Assertions.assertEquals(1, decodedSwaggerArgs.size()); - decodedArgs = (Map) decodedSwaggerArgs.get("value"); + decodedArgs = (Map) decodedSwaggerArgs.get("listListUserBody"); } else { decodedArgs = decodedSwaggerArgs; } @@ -494,7 +491,7 @@ public void testProtoSchemaOperationObjPOJO() throws Exception { private void testProtoSchemaOperationObjImpl(boolean isPojo) throws IOException { Invocation consumerInvocation = mockInvocation("obj", InvocationType.CONSUMER); - Invocation providerInvocation = mockInvocation("obj", InvocationType.PRODUCER); + Invocation providerInvocation = mockInvocation("obj", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodecRestTemplate.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodecRestTemplate.java index 774d61ab2d5..2a861d202be 100644 --- a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodecRestTemplate.java +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodecRestTemplate.java @@ -53,7 +53,6 @@ import org.mockito.Mockito; import com.fasterxml.jackson.databind.type.TypeFactory; -import io.swagger.models.Swagger; /** * SchemaMetaCodec test cases. This test cases covers RestTemplate invoker and producer. @@ -85,33 +84,33 @@ public void setUp() { Mockito.when(consumerMicroserviceMeta.getExtData(ProtobufManager.EXT_ID)).thenReturn(null); SpringmvcSwaggerGenerator swaggerGenerator = new SpringmvcSwaggerGenerator(ProtoSchema.class); - Swagger swagger = swaggerGenerator.generate(); SwaggerEnvironment swaggerEnvironment = new SwaggerEnvironment(); - providerSchemaMeta = new SchemaMeta(providerMicroserviceMeta, "ProtoSchema", swagger); - SwaggerProducer swaggerProducer = swaggerEnvironment.createProducer(new ProtoSchema(), swagger); + SwaggerProducer swaggerProducer = swaggerEnvironment.createProducer(new ProtoSchema()); + providerSchemaMeta = new SchemaMeta(providerMicroserviceMeta, "ProtoSchema", swaggerProducer.getSwagger()); + for (SwaggerProducerOperation producerOperation : swaggerProducer.getAllOperations()) { OperationMeta operationMeta = providerSchemaMeta.ensureFindOperation(producerOperation.getOperationId()); operationMeta.setSwaggerProducerOperation(producerOperation); } - consumerSchemaMeta = new SchemaMeta(consumerMicroserviceMeta, "ProtoSchema", swagger); + consumerSchemaMeta = new SchemaMeta(consumerMicroserviceMeta, "ProtoSchema", swaggerProducer.getSwagger()); } private Invocation mockInvocation(String operation, InvocationType invocationType) { OperationMeta operationMeta; - boolean isConsumer; + boolean isProvider; Invocation invocation = Mockito.mock(Invocation.class); InvocationRuntimeType invocationRuntimeType; if (InvocationType.CONSUMER == invocationType) { operationMeta = consumerSchemaMeta.getOperations().get(operation); - isConsumer = true; + isProvider = false; Mockito.when(invocation.getSchemaMeta()).thenReturn(consumerSchemaMeta); invocationRuntimeType = operationMeta.buildBaseConsumerRuntimeType(); } else { operationMeta = providerSchemaMeta.getOperations().get(operation); - isConsumer = false; + isProvider = true; Mockito.when(invocation.getSchemaMeta()).thenReturn(providerSchemaMeta); invocationRuntimeType = operationMeta.buildBaseProviderRuntimeType(); } @@ -125,14 +124,14 @@ private Invocation mockInvocation(String operation, InvocationType invocationTyp Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); Mockito.when(invocation.getMicroserviceMeta()).thenReturn(microserviceMeta); - Mockito.when(invocation.isConsumer()).thenReturn(isConsumer); + Mockito.when(invocation.isProducer()).thenReturn(isProvider); return invocation; } @Test public void testProtoSchemaOperationUser() throws Exception { Invocation consumerInvocation = mockInvocation("user", InvocationType.CONSUMER); - Invocation providerInvocation = mockInvocation("user", InvocationType.PRODUCER); + Invocation providerInvocation = mockInvocation("user", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); @@ -181,7 +180,7 @@ public void testProtoSchemaOperationUser() throws Exception { @SuppressWarnings({"rawtypes", "unchecked"}) public void testProtoSchemaOperationBase() throws Exception { Invocation consumerInvocation = mockInvocation("base", InvocationType.CONSUMER); - Invocation providerInvocation = mockInvocation("base", InvocationType.PRODUCER); + Invocation providerInvocation = mockInvocation("base", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSwaggerToProtoGenerator.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSwaggerToProtoGenerator.java index 4110cc011f7..535404f7bc0 100644 --- a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSwaggerToProtoGenerator.java +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSwaggerToProtoGenerator.java @@ -16,17 +16,18 @@ */ package org.apache.servicecomb.codec.protobuf.internal.converter; -import io.protostuff.compiler.model.Proto; -import io.swagger.models.Swagger; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; + import org.apache.commons.io.IOUtils; import org.apache.servicecomb.codec.protobuf.internal.converter.model.ProtoSchema; import org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGenerator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.io.IOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; +import io.protostuff.compiler.model.Proto; +import io.swagger.v3.oas.models.OpenAPI; public class TestSwaggerToProtoGenerator { @Test @@ -37,7 +38,7 @@ public void convert() throws IOException { protoContent = protoContent.substring(idx); SpringmvcSwaggerGenerator swaggerGenerator = new SpringmvcSwaggerGenerator(ProtoSchema.class); - Swagger swagger = swaggerGenerator.generate(); + OpenAPI swagger = swaggerGenerator.generate(); SwaggerToProtoGenerator generator = new SwaggerToProtoGenerator("a.b", swagger); Proto proto = generator.convert(); diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchema.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchema.java index ae4f2dae190..169e742c9b5 100644 --- a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchema.java +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchema.java @@ -30,12 +30,16 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.ws.rs.core.MediaType; @RequestMapping(path = "/") public class ProtoSchema implements ProtoSchemaIntf { - @ApiResponses(value = {@ApiResponse(code = 444, response = Color.class, message = "xxx")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "444", content = @Content(schema = @Schema(implementation = Color.class)), description = "xxx")}) @GetMapping(path = "/base") public int base(boolean boolValue, int iValue, long lValue, float fValue, double dValue, String sValue, int[] iArray, Color color, @@ -174,4 +178,9 @@ public Map>> mapMapMapString( public FieldNeedWrap fieldNeedWrap(@RequestBody FieldNeedWrap fieldNeedWrap) { return fieldNeedWrap; } + + @PostMapping(path = "/testTextPlain", consumes = MediaType.TEXT_PLAIN, produces = MediaType.TEXT_PLAIN) + public String testTextPlain(@RequestBody String fieldNeedWrap) { + return null; + } } diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchemaPojo.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchemaPojo.java index 8f6a61212d9..af01e2e2325 100644 --- a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchemaPojo.java +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchemaPojo.java @@ -26,11 +26,14 @@ import org.apache.servicecomb.foundation.test.scaffolding.model.Empty; import org.apache.servicecomb.foundation.test.scaffolding.model.User; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; public class ProtoSchemaPojo implements ProtoSchemaIntf { - @ApiResponses(value = {@ApiResponse(code = 444, response = Color.class, message = "xxx")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "444", content = @Content(schema = @Schema(implementation = Color.class)), description = "xxx")}) public int base(boolean boolValue, int iValue, long lValue, float fValue, double dValue, String sValue, int[] iArray, Color color, LocalDate localDate, Date date, Empty empty) { diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/TestSchemaCodec.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/TestSchemaCodec.java new file mode 100644 index 00000000000..9eab70ae00a --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/TestSchemaCodec.java @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.codec.protobuf.schema; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.codec.protobuf.schema.model.DeptInfo; +import org.apache.servicecomb.codec.protobuf.schema.model.SchemaService; +import org.apache.servicecomb.codec.protobuf.schema.model.ScoreInfo; +import org.apache.servicecomb.codec.protobuf.schema.model.UserInfo; +import org.apache.servicecomb.codec.protobuf.utils.ScopedProtobufSchemaManager; +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.RootSerializer; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; +import org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGenerator; +import org.junit.jupiter.api.Test; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.NumberSchema; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.media.StringSchema; +import jakarta.ws.rs.core.MediaType; + +public class TestSchemaCodec { + ScopedProtobufSchemaManager manager = new ScopedProtobufSchemaManager(); + + @Test + public void test_string_schema_codec() throws Exception { + OpenAPI openAPI = new OpenAPI(); + StringSchema schema = new StringSchema(); + ProtoMapper protoMapper = manager.getOrCreateProtoMapper(openAPI, "test", "input", schema); + RootSerializer serializer = protoMapper.getSerializerSchemaManager() + .createRootSerializer(protoMapper.getProto().getMessage("input"), + String.class); + Map arguments = new HashMap<>(); + arguments.put("value", "abcdefg"); + byte[] result = serializer.serialize(arguments); + RootDeserializer> deserializer = protoMapper.getDeserializerSchemaManager() + .createRootDeserializer(protoMapper.getProto().getMessage("input"), String.class); + PropertyWrapper deserializedResult = deserializer.deserialize(result); + assertEquals("abcdefg", deserializedResult.getValue()); + } + + @Test + public void test_number_schema_codec() throws Exception { + OpenAPI openAPI = new OpenAPI(); + NumberSchema schema = new NumberSchema(); + ProtoMapper protoMapper = manager.getOrCreateProtoMapper(openAPI, "test", "input", schema); + RootSerializer serializer = protoMapper.getSerializerSchemaManager() + .createRootSerializer(protoMapper.getProto().getMessage("input"), + BigDecimal.class); + Map arguments = new HashMap<>(); + BigDecimal number = new BigDecimal(10); + arguments.put("value", number); + byte[] result = serializer.serialize(arguments); + RootDeserializer> deserializer = protoMapper.getDeserializerSchemaManager() + .createRootDeserializer(protoMapper.getProto().getMessage("input"), BigDecimal.class); + PropertyWrapper deserializedResult = deserializer.deserialize(result); + assertEquals(number, deserializedResult.getValue()); + } + + public static class User { + public String name; + } + + @Test + public void test_object_schema_codec() throws Exception { + OpenAPI openAPI = new OpenAPI(); + + ObjectSchema schema = new ObjectSchema(); + schema.setName("User"); + schema.addProperty("name", new StringSchema()); + openAPI.setComponents(new Components()); + openAPI.getComponents().addSchemas("User", schema); + + ObjectSchema ref = new ObjectSchema(); + ref.set$ref(Components.COMPONENTS_SCHEMAS_REF + "User"); + + ProtoMapper protoMapper = manager.getOrCreateProtoMapper(openAPI, "test", "input", ref); + RootSerializer serializer = protoMapper.getSerializerSchemaManager() + .createRootSerializer(protoMapper.getProto().getMessage("input"), + User.class); + Map arguments = new HashMap<>(); + User user = new User(); + user.name = "abcdefg"; + arguments.put("value", user); + byte[] result = serializer.serialize(arguments); + RootDeserializer> deserializer = protoMapper.getDeserializerSchemaManager() + .createRootDeserializer(protoMapper.getProto().getMessage("input"), User.class); + PropertyWrapper deserializedResult = deserializer.deserialize(result); + assertEquals("abcdefg", deserializedResult.getValue().name); + } + + @Test + public void test_springmvc_model_schema_codec_correct() throws Exception { + SpringmvcSwaggerGenerator generator = new SpringmvcSwaggerGenerator(SchemaService.class); + OpenAPI openAPI = generator.generate(); + + ProtoMapper protoMapper = manager.getOrCreateProtoMapper(openAPI, "schemaService", "input", + openAPI.getPaths().get("/testUserInfo").getPost() + .getRequestBody().getContent().get(MediaType.APPLICATION_JSON) + .getSchema()); + RootSerializer serializer = protoMapper.getSerializerSchemaManager() + .createRootSerializer(protoMapper.getProto().getMessage("input"), + UserInfo.class); + Map arguments = new HashMap<>(); + UserInfo userInfo = new UserInfo(); + DeptInfo deptInfo = new DeptInfo(); + deptInfo.setCode("123"); + ScoreInfo scoreInfo = new ScoreInfo(); + scoreInfo.setType(233); + deptInfo.setScores(List.of(scoreInfo)); + userInfo.setSubDeptInfos(List.of(deptInfo)); + arguments.put("value", userInfo); + byte[] result = serializer.serialize(arguments); + RootDeserializer> deserializer = protoMapper.getDeserializerSchemaManager() + .createRootDeserializer(protoMapper.getProto().getMessage("input"), UserInfo.class); + PropertyWrapper deserializedResult = deserializer.deserialize(result); + assertEquals(1, deserializedResult.getValue().getSubDeptInfos().size()); + assertEquals("123", deserializedResult.getValue().getSubDeptInfos().get(0).getCode()); + assertEquals(233, deserializedResult.getValue().getSubDeptInfos().get(0).getScores().get(0).getType()); + } +} diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/TestSchemaToProtoGenerator.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/TestSchemaToProtoGenerator.java new file mode 100644 index 00000000000..23ac41135ad --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/TestSchemaToProtoGenerator.java @@ -0,0 +1,306 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.codec.protobuf.schema; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.servicecomb.codec.protobuf.internal.converter.ProtoToStringGenerator; +import org.apache.servicecomb.codec.protobuf.schema.model.SchemaService; +import org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGenerator; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import io.protostuff.compiler.model.Proto; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.media.StringSchema; +import jakarta.ws.rs.core.MediaType; + +//CHECKSTYLE:OFF +@SuppressWarnings("unused") +public class TestSchemaToProtoGenerator { + @Test + public void test_string_schema_is_correct() { + OpenAPI openAPI = new OpenAPI(); + StringSchema schema = new StringSchema(); + SchemaToProtoGenerator generator = + new SchemaToProtoGenerator("test.string", openAPI, schema, "input"); + Proto proto = generator.convert(); + assertEquals(""" + syntax = "proto3"; + package test.string; + + //@WrapProperty + message input { + string value = 1; + } + """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); + } + + @Test + public void test_object_schema_is_correct() { + OpenAPI openAPI = new OpenAPI(); + + ObjectSchema schema = new ObjectSchema(); + schema.setName("User"); + schema.addProperty("name", new StringSchema()); + openAPI.setComponents(new Components()); + openAPI.getComponents().addSchemas("User", schema); + + ObjectSchema ref = new ObjectSchema(); + ref.set$ref(Components.COMPONENTS_SCHEMAS_REF + "User"); + + SchemaToProtoGenerator generator = + new SchemaToProtoGenerator("test.object", openAPI, ref, "input"); + Proto proto = generator.convert(); + assertEquals(""" + syntax = "proto3"; + package test.object; + + message User { + string name = 1; + } + + //@WrapProperty + message input { + User value = 1; + } + """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); + } + + static class Model { + public String name; + + public int age; + } + + interface SpringMvcSchema { + @PostMapping("/testInt") + int testInt(@RequestBody int param); + + @PostMapping("/testModel") + Model testModel(@RequestBody Model model); + } + + @Test + public void test_springmvc_int_schema_correct() { + SpringmvcSwaggerGenerator generator = new SpringmvcSwaggerGenerator(SpringMvcSchema.class); + OpenAPI openAPI = generator.generate(); + + SchemaToProtoGenerator protoGenerator = + new SchemaToProtoGenerator("test.int", openAPI, + openAPI.getPaths().get("/testInt").getPost() + .getRequestBody().getContent().get(MediaType.APPLICATION_JSON) + .getSchema(), "testIntRequest"); + Proto proto = protoGenerator.convert(); + assertEquals(""" + syntax = "proto3"; + package test.int; + + //@WrapProperty + message testIntRequest { + sint32 value = 1; + } + """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); + + protoGenerator = + new SchemaToProtoGenerator("test.int", openAPI, + openAPI.getPaths().get("/testInt").getPost() + .getResponses().get("200").getContent().get(MediaType.APPLICATION_JSON) + .getSchema(), "testIntResponse"); + proto = protoGenerator.convert(); + assertEquals(""" + syntax = "proto3"; + package test.int; + + //@WrapProperty + message testIntResponse { + sint32 value = 1; + } + """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); + } + + + @Test + public void test_springmvc_model_schema_correct() { + SpringmvcSwaggerGenerator generator = new SpringmvcSwaggerGenerator(SpringMvcSchema.class); + OpenAPI openAPI = generator.generate(); + + SchemaToProtoGenerator protoGenerator = + new SchemaToProtoGenerator("test.model", openAPI, + openAPI.getPaths().get("/testModel").getPost() + .getRequestBody().getContent().get(MediaType.APPLICATION_JSON) + .getSchema(), "testModelRequest"); + Proto proto = protoGenerator.convert(); + assertEquals(""" + syntax = "proto3"; + package test.model; + + message Model { + sint32 age = 1; + string name = 2; + } + + //@WrapProperty + message testModelRequest { + Model value = 1; + } + """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); + + protoGenerator = + new SchemaToProtoGenerator("test.model", openAPI, + openAPI.getPaths().get("/testModel").getPost() + .getResponses().get("200").getContent().get(MediaType.APPLICATION_JSON) + .getSchema(), "testIntResponse"); + proto = protoGenerator.convert(); + assertEquals(""" + syntax = "proto3"; + package test.model; + + message Model { + sint32 age = 1; + string name = 2; + } + + //@WrapProperty + message testIntResponse { + Model value = 1; + } + """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); + } + + @Test + public void testNestedModelCorrect() { + SpringmvcSwaggerGenerator generator = new SpringmvcSwaggerGenerator(SchemaService.class); + OpenAPI openAPI = generator.generate(); + SchemaToProtoGenerator protoGenerator = + new SchemaToProtoGenerator("test.model", openAPI, + openAPI.getPaths().get("/testUserInfo").getPost() + .getRequestBody().getContent().get(MediaType.APPLICATION_JSON) + .getSchema(), "request"); + Proto proto = protoGenerator.convert(); + assertEquals(""" + syntax = "proto3"; + package test.model; + + //@WrapProperty + message MapString { + map value = 1; + } + + //@WrapProperty + message ListListString { + repeated ListString value = 1; + } + + //@WrapProperty + message ListString { + repeated string value = 1; + } + + message ScoreInfo { + sint32 type = 1; + } + + message DeptInfo { + string code = 1; + string name = 2; + repeated ScoreInfo scores = 3; + } + + message UserInfo { + repeated MapString extraInfos = 1; + repeated ListListString nestedLists = 2; + repeated DeptInfo subDeptInfos = 3; + } + + //@WrapProperty + message ListScoreInfo { + repeated ScoreInfo value = 1; + } + + //@WrapProperty + message ListDeptInfo { + repeated DeptInfo value = 1; + } + + //@WrapProperty + message request { + UserInfo value = 1; + } + """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); + } + + @Test + public void testListMapTypeCorrect() { + SpringmvcSwaggerGenerator generator = new SpringmvcSwaggerGenerator(SchemaService.class); + OpenAPI openAPI = generator.generate(); + SchemaToProtoGenerator protoGenerator = + new SchemaToProtoGenerator("test.model", openAPI, + openAPI.getPaths().get("/testListType").getPost() + .getRequestBody().getContent().get(MediaType.APPLICATION_JSON) + .getSchema(), "request"); + Proto proto = protoGenerator.convert(); + assertEquals(""" + syntax = "proto3"; + package test.model; + + message ScoreInfo { + sint32 type = 1; + } + + message DeptInfo { + string code = 1; + string name = 2; + repeated ScoreInfo scores = 3; + } + + //@WrapProperty + message ListScoreInfo { + repeated ScoreInfo value = 1; + } + + //@WrapProperty + message ListDeptInfo { + repeated DeptInfo value = 1; + } + + //@WrapProperty + message request { + repeated DeptInfo value = 1; + } + """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); + } + + @Test + public void testCyclicModelWrong() { + SpringmvcSwaggerGenerator generator = new SpringmvcSwaggerGenerator(SchemaService.class); + OpenAPI openAPI = generator.generate(); + SchemaToProtoGenerator protoGenerator = + new SchemaToProtoGenerator("test.model", openAPI, + openAPI.getPaths().get("/testCyclic").getPost() + .getRequestBody().getContent().get(MediaType.APPLICATION_JSON) + .getSchema(), "request"); + IllegalArgumentException throwable = Assertions.catchThrowableOfType(() -> protoGenerator.convert(), + IllegalArgumentException.class); + assertEquals("Failed to create schema request. May be cyclic object.", throwable.getMessage()); + } +} +//CHECKSTYLE:ON diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/CyclicInfo.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/CyclicInfo.java new file mode 100644 index 00000000000..ae8f2fbd5c0 --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/CyclicInfo.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.servicecomb.codec.protobuf.schema.model; + +public class CyclicInfo { + private String name; + private CyclicInfo child; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public CyclicInfo getChild() { + return child; + } + + public void setChild(CyclicInfo child) { + this.child = child; + } +} diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/DeptInfo.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/DeptInfo.java new file mode 100644 index 00000000000..713b45e0224 --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/DeptInfo.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.codec.protobuf.schema.model; + +import java.util.List; + +public class DeptInfo { + private String name; + + private String code; + + private List scores; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public List getScores() { + return scores; + } + + public void setScores(List scores) { + this.scores = scores; + } +} diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/SchemaService.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/SchemaService.java new file mode 100644 index 00000000000..99edc87023d --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/SchemaService.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.servicecomb.codec.protobuf.schema.model; + +import java.util.List; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping("/schemaService") +public class SchemaService { + @PostMapping("testUserInfo") + public void testUserInfo(@RequestBody UserInfo request) { + } + + @PostMapping("testListType") + public void testListType(@RequestBody List request) { + } + + @PostMapping("testCyclic") + public void testCyclic(@RequestBody CyclicInfo request) { + } +} diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/ScoreInfo.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/ScoreInfo.java new file mode 100644 index 00000000000..960ce778857 --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/ScoreInfo.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.servicecomb.codec.protobuf.schema.model; + +public class ScoreInfo { + private Integer type; + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } +} diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/UserInfo.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/UserInfo.java new file mode 100644 index 00000000000..37662bb32ed --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/UserInfo.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.servicecomb.codec.protobuf.schema.model; + +import java.util.List; +import java.util.Map; + +public class UserInfo { + private List subDeptInfos; + + private List> extraInfos; + + private List>> nestedLists; + + public List getSubDeptInfos() { + return subDeptInfos; + } + + public void setSubDeptInfos(List subDeptInfos) { + this.subDeptInfos = subDeptInfos; + } + + public List> getExtraInfos() { + return extraInfos; + } + + public void setExtraInfos(List> extraInfos) { + this.extraInfos = extraInfos; + } + + public List>> getNestedLists() { + return nestedLists; + } + + public void setNestedLists(List>> nestedLists) { + this.nestedLists = nestedLists; + } +} diff --git a/common/common-protobuf/src/test/proto/ModelProtobuf.proto b/common/common-protobuf/src/test/proto/ModelProtobuf.proto index ec2a5d803a9..c1fb2ed34dd 100644 --- a/common/common-protobuf/src/test/proto/ModelProtobuf.proto +++ b/common/common-protobuf/src/test/proto/ModelProtobuf.proto @@ -5,9 +5,9 @@ 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. diff --git a/common/common-protobuf/src/test/resources/ProtoSchema.proto b/common/common-protobuf/src/test/resources/ProtoSchema.proto index cb78e18d890..bbb1a476948 100644 --- a/common/common-protobuf/src/test/resources/ProtoSchema.proto +++ b/common/common-protobuf/src/test/resources/ProtoSchema.proto @@ -52,12 +52,12 @@ message Ref2 { //@WrapArguments message BaseRequestWrap { bool boolValue = 1; - int32 iValue = 2; - int64 lValue = 3; + sint32 iValue = 2; + sint64 lValue = 3; float fValue = 4; double dValue = 5; string sValue = 6; - repeated int32 iArray = 7; + repeated sint32 iArray = 7; Enum_2610aa5dc6cd086cf20168892802c9c765a557f4951557340ad9f0982c53e055 color = 8; int64 localDate = 9; int64 date = 10; @@ -65,13 +65,13 @@ message BaseRequestWrap { } //@WrapProperty -message BaseResponseWrap444 { - Enum_2610aa5dc6cd086cf20168892802c9c765a557f4951557340ad9f0982c53e055 value = 1; +message BaseResponseWrap200 { + sint32 value = 1; } //@WrapProperty -message BaseResponseWrap200 { - int32 value = 1; +message BaseResponseWrap444 { + Enum_2610aa5dc6cd086cf20168892802c9c765a557f4951557340ad9f0982c53e055 value = 1; } //@WrapArguments @@ -274,10 +274,20 @@ message MapUserResponseWrap200 { map value = 1; } +//@WrapArguments +message TestTextPlainRequestWrap { + string fieldNeedWrap = 1; +} + +//@WrapProperty +message TestTextPlainResponseWrap200 { + string value = 1; +} + //@WrapArguments message UserWrapInProtobufRequestWrap { - User user = 1; - int32 ivalue = 2; + sint32 ivalue = 1; + User user = 2; } //@WrapProperty @@ -422,6 +432,9 @@ service MainService { //@Rpc{"argTypeName":"Ref1","responses":{"200":{"typeName":"Ref2"}}} rpc ref (Ref1) returns (Ref2); + //@Rpc{"argTypeName":"TestTextPlainRequestWrap","responses":{"200":{"typeName":"TestTextPlainResponseWrap200"}}} + rpc testTextPlain (TestTextPlainRequestWrap) returns (TestTextPlainResponseWrap200); + //@Rpc{"argTypeName":"User","responses":{"200":{"typeName":"User"}}} rpc user (User) returns (User); diff --git a/common/common-rest/pom.xml b/common/common-rest/pom.xml index 1233e8fed3c..f1f2c84b45b 100644 --- a/common/common-rest/pom.xml +++ b/common/common-rest/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb common - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT common-rest @@ -34,6 +34,10 @@ org.apache.servicecomb java-chassis-core + + org.apache.servicecomb + common-protobuf + io.vertx diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/CommonRestConfiguration.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/CommonRestConfiguration.java index a529541909f..a642265ae0f 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/CommonRestConfiguration.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/CommonRestConfiguration.java @@ -20,61 +20,60 @@ import org.apache.servicecomb.common.rest.codec.query.QueryCodec; import org.apache.servicecomb.common.rest.codec.query.QueryCodecCsv; -import org.apache.servicecomb.common.rest.codec.query.QueryCodecJson; import org.apache.servicecomb.common.rest.codec.query.QueryCodecMulti; import org.apache.servicecomb.common.rest.codec.query.QueryCodecPipes; import org.apache.servicecomb.common.rest.codec.query.QueryCodecSsv; -import org.apache.servicecomb.common.rest.codec.query.QueryCodecTsv; import org.apache.servicecomb.common.rest.codec.query.QueryCodecs; import org.apache.servicecomb.common.rest.codec.query.QueryCodecsUtils; import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter; +import org.apache.servicecomb.common.rest.filter.inner.WebSocketServerCodecFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CommonRestConfiguration { @Bean - public QueryCodecCsv queryCodecCsv() { + public QueryCodecCsv scbQueryCodecCsv() { return new QueryCodecCsv(); } @Bean - public QueryCodecTsv queryCodecTsv() { - return new QueryCodecTsv(); - } - - @Bean - public QueryCodecSsv queryCodecSsv() { + public QueryCodecSsv scbQueryCodecSsv() { return new QueryCodecSsv(); } @Bean - public QueryCodecPipes queryCodecPipes() { + public QueryCodecPipes scbQueryCodecPipes() { return new QueryCodecPipes(); } @Bean - public QueryCodecMulti queryCodecMulti() { + public QueryCodecMulti scbQueryCodecMulti() { return new QueryCodecMulti(); } @Bean - public QueryCodecJson queryCodecJson() { - return new QueryCodecJson(); + public QueryCodecsUtils scbQueryCodecsUtils(QueryCodecs queryCodecs) { + return new QueryCodecsUtils(queryCodecs); } @Bean - public QueryCodecsUtils queryCodecsUtils(QueryCodecs queryCodecs) { - return new QueryCodecsUtils(queryCodecs); + public RestServerCodecFilter scbRestServerCodecFilter() { + return new RestServerCodecFilter(); } @Bean - public RestServerCodecFilter restServerCodecFilter() { - return new RestServerCodecFilter(); + public WebSocketServerCodecFilter scbWebSocketServerCodecFilter() { + return new WebSocketServerCodecFilter(); } @Bean - public QueryCodecs queryCodecs(List orderedCodecs) { + public QueryCodecs scbQueryCodecs(List orderedCodecs) { return new QueryCodecs(orderedCodecs); } + + @Bean + public RestEngineSchemaListener scbRestEngineSchemaListener() { + return new RestEngineSchemaListener(); + } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/EdgeServerWebSocketInvocationCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/EdgeServerWebSocketInvocationCreator.java new file mode 100644 index 00000000000..ff0b0f38174 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/EdgeServerWebSocketInvocationCreator.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.servicecomb.common.rest; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.common.rest.locator.OperationLocator; +import org.apache.servicecomb.common.rest.locator.ServicePathManager; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.invocation.InvocationFactory; +import org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig; +import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; + +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.ServerWebSocket; + +public class EdgeServerWebSocketInvocationCreator extends ProviderServerWebSocketInvocationCreator { + private final String microserviceName; + + protected MicroserviceReferenceConfig microserviceReferenceConfig; + + protected final String path; + + public EdgeServerWebSocketInvocationCreator(String microserviceName, String path, + Endpoint endpoint, ServerWebSocket webSocket) { + super(null, endpoint, webSocket); + this.microserviceName = microserviceName; + this.path = path; + } + + @Override + public CompletableFuture createAsync() { + return createMicroserviceReferenceConfig() + .thenCompose(v -> super.createAsync()); + } + + protected CompletableFuture createMicroserviceReferenceConfig() { + return SCBEngine.getInstance() + .getOrCreateReferenceConfigAsync(microserviceName) + .thenAccept(mrc -> { + this.microserviceReferenceConfig = mrc; + this.microserviceMeta = mrc.getMicroserviceMeta(); + }); + } + + @Override + protected OperationLocator locateOperation(ServicePathManager servicePathManager) { + return servicePathManager.consumerLocateOperation(path, HttpMethod.POST.name()); + } + + @Override + protected Invocation createInstance() { + ReferenceConfig referenceConfig = microserviceReferenceConfig + .createReferenceConfig(restOperationMeta.getOperationMeta()); + + Invocation invocation = InvocationFactory.forConsumer(referenceConfig, + restOperationMeta.getOperationMeta(), + restOperationMeta.getOperationMeta().buildBaseConsumerRuntimeType(), + null); + invocation.setSync(false); + invocation.setEdge(); + invocation.setEndpoint(endpoint); // ensure transport name is correct + + return invocation; + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/HttpTransportContext.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/HttpTransportContext.java index b9c8ea20a26..d9b0df27c92 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/HttpTransportContext.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/HttpTransportContext.java @@ -16,7 +16,6 @@ */ package org.apache.servicecomb.common.rest; -import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.context.TransportContext; @@ -26,13 +25,9 @@ public class HttpTransportContext implements TransportContext { private final HttpServletResponseEx responseEx; - private final ProduceProcessor produceProcessor; - - public HttpTransportContext(HttpServletRequestEx requestEx, HttpServletResponseEx responseEx, - ProduceProcessor produceProcessor) { + public HttpTransportContext(HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { this.requestEx = requestEx; this.responseEx = responseEx; - this.produceProcessor = produceProcessor; } public HttpServletRequestEx getRequestEx() { @@ -42,8 +37,4 @@ public HttpServletRequestEx getRequestEx() { public HttpServletResponseEx getResponseEx() { return responseEx; } - - public ProduceProcessor getProduceProcessor() { - return produceProcessor; - } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ProviderServerWebSocketInvocationCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ProviderServerWebSocketInvocationCreator.java new file mode 100644 index 00000000000..bcd4fc827b3 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ProviderServerWebSocketInvocationCreator.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.MicroserviceMeta; + +import io.vertx.core.http.ServerWebSocket; + +public class ProviderServerWebSocketInvocationCreator extends ServerWebSocketInvocationCreator { + public ProviderServerWebSocketInvocationCreator(MicroserviceMeta microserviceMeta, + Endpoint endpoint, ServerWebSocket webSocket) { + super(microserviceMeta, endpoint, webSocket); + } + + @Override + protected void initTransportContext(Invocation invocation) { + WebSocketTransportContext transportContext = new WebSocketTransportContext(websocket); + invocation.setTransportContext(transportContext); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestConst.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestConst.java index 3d0bd954ab0..bc598f973f6 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestConst.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestConst.java @@ -43,17 +43,14 @@ private RestConst() { // in HttpServletRequest attribute public static final String BODY_PARAMETER = "servicecomb-body"; - // in HttpServletRequest attribute - public static final String FORM_PARAMETERS = "servicecomb-forms"; - //in invocation response - public static final String INVOCATION_HANDLER_RESPONSE = "servicecomb-invocation-hanlder-response"; + public static final String INVOCATION_HANDLER_RESPONSE = "servicecomb-invocation-handler-response"; //in invocation response - public static final String INVOCATION_HANDLER_PROCESSOR = "servicecomb-invocation-hanlder-processor"; + public static final String INVOCATION_HANDLER_PROCESSOR = "servicecomb-invocation-handler-processor"; //in invocation response - public static final String INVOCATION_HANDLER_REQUESTCLIENT = "servicecomb-invocation-hanlder-requestclient"; + public static final String INVOCATION_HANDLER_REQUESTCLIENT = "servicecomb-invocation-handler-requestclient"; public static final String REST_PRODUCER_INVOCATION = "servicecomb-rest-producer-invocation"; @@ -86,4 +83,6 @@ private RestConst() { public static final String HEADER_CONTEXT_MAPPER = "servicecomb.context.headerContextMapper"; public static final String QUERY_CONTEXT_MAPPER = "servicecomb.context.queryContextMapper"; + + public static final String DECODE_INVOCATION_CONTEXT = "servicecomb.context.decodeInvocationContext"; } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestEngineSchemaListener.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestEngineSchemaListener.java index 51d68a8b07a..178d52f614d 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestEngineSchemaListener.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestEngineSchemaListener.java @@ -19,11 +19,9 @@ import org.apache.servicecomb.common.rest.locator.ServicePathManager; import org.apache.servicecomb.core.BootListener; -import org.apache.servicecomb.core.definition.CoreMetaUtils; +import org.apache.servicecomb.core.SCBEngine.CreateMicroserviceMetaEvent; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.foundation.common.event.EnableExceptionPropagation; -import org.apache.servicecomb.registry.consumer.MicroserviceVersion; -import org.apache.servicecomb.registry.api.event.CreateMicroserviceVersionEvent; import com.google.common.eventbus.Subscribe; @@ -42,9 +40,8 @@ public void onBeforeRegistry(BootEvent event) { @EnableExceptionPropagation @Subscribe - public void onCreateMicroserviceVersion(CreateMicroserviceVersionEvent event) { - MicroserviceVersion microserviceVersion = event.getMicroserviceVersion(); - MicroserviceMeta microserviceMeta = CoreMetaUtils.getMicroserviceMeta(microserviceVersion); + public void onCreateMicroserviceMetaEvent(CreateMicroserviceMetaEvent event) { + MicroserviceMeta microserviceMeta = event.getMicroserviceMeta(); createServicePathManager(microserviceMeta); } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java index 3d3f4f0ca47..5a6c09d3912 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java @@ -16,39 +16,32 @@ */ package org.apache.servicecomb.common.rest; -import static javax.ws.rs.core.Response.Status.NOT_ACCEPTABLE; -import static javax.ws.rs.core.Response.Status.NOT_FOUND; -import static org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_CLIENT; +import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; import static org.apache.servicecomb.core.exception.ExceptionCodes.NOT_DEFINED_ANY_SCHEMA; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; -import javax.ws.rs.core.HttpHeaders; - import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.common.rest.locator.ServicePathManager; import org.apache.servicecomb.config.YAMLUtil; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.core.invocation.InvocationFactory; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.annotations.VisibleForTesting; -import com.netflix.config.DynamicPropertyFactory; - import io.vertx.core.json.Json; public abstract class RestProducerInvocationCreator implements InvocationCreator { @@ -67,7 +60,7 @@ public abstract class RestProducerInvocationCreator implements InvocationCreator protected ProduceProcessor produceProcessor; public RestProducerInvocationCreator(MicroserviceMeta microserviceMeta, Endpoint endpoint, - @Nonnull HttpServletRequestEx requestEx, @Nonnull HttpServletResponseEx responseEx) { + HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { this.microserviceMeta = microserviceMeta; this.endpoint = endpoint; this.requestEx = requestEx; @@ -81,7 +74,6 @@ public CompletableFuture createAsync() { Invocation invocation = createInstance(); initInvocationContext(invocation); addParameterContext(invocation); - initProduceProcessor(); initTransportContext(invocation); invocation.addLocalContext(RestConst.REST_REQUEST, requestEx); @@ -94,7 +86,11 @@ protected Invocation createInstance() { } protected void initInvocationContext(Invocation invocation) { - String strCseContext = requestEx.getHeader(Const.CSE_CONTEXT); + if (!LegacyPropertyFactory.getBooleanProperty(RestConst.DECODE_INVOCATION_CONTEXT, true)) { + return; + } + + String strCseContext = requestEx.getHeader(CoreConst.CSE_CONTEXT); if (StringUtils.isEmpty(strCseContext)) { return; } @@ -105,10 +101,10 @@ protected void initInvocationContext(Invocation invocation) { } protected void addParameterContext(Invocation invocation) { - String headerContextMapper = DynamicPropertyFactory.getInstance() - .getStringProperty(RestConst.HEADER_CONTEXT_MAPPER, null).get(); - String queryContextMapper = DynamicPropertyFactory.getInstance() - .getStringProperty(RestConst.QUERY_CONTEXT_MAPPER, null).get(); + String headerContextMapper = LegacyPropertyFactory + .getStringProperty(RestConst.HEADER_CONTEXT_MAPPER); + String queryContextMapper = LegacyPropertyFactory + .getStringProperty(RestConst.QUERY_CONTEXT_MAPPER); Map headerContextMappers; if (headerContextMapper != null) { @@ -158,16 +154,4 @@ protected OperationLocator locateOperation(MicroserviceMeta microserviceMeta) { protected OperationLocator locateOperation(ServicePathManager servicePathManager) { return servicePathManager.producerLocateOperation(requestEx.getRequestURI(), requestEx.getMethod()); } - - @VisibleForTesting - void initProduceProcessor() { - produceProcessor = restOperationMeta.ensureFindProduceProcessor(requestEx); - if (produceProcessor == null) { - LOGGER.error("Accept {} is not supported, operation={}.", requestEx.getHeader(HttpHeaders.ACCEPT), - restOperationMeta.getOperationMeta().getMicroserviceQualifiedName()); - - String msg = String.format("Accept %s is not supported", requestEx.getHeader(HttpHeaders.ACCEPT)); - throw Exceptions.create(NOT_ACCEPTABLE, GENERIC_CLIENT, msg); - } - } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java index 70c3a5a3a01..727346c82d5 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java @@ -16,8 +16,6 @@ */ package org.apache.servicecomb.common.rest; -import static org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter.isDownloadFileResponseType; - import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager; import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter; @@ -25,7 +23,6 @@ import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.core.invocation.ProducerInvocationFlow; -import org.apache.servicecomb.foundation.common.utils.PartUtils; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.Response; @@ -47,40 +44,42 @@ public RestProducerInvocationFlow(InvocationCreator invocationCreator, protected Invocation sendCreateInvocationException(Throwable throwable) { try { Response response = Exceptions.toProducerResponse(null, throwable); - RestServerCodecFilter.encodeResponse(response, false, DEFAULT_PRODUCE_PROCESSOR, responseEx); + RestServerCodecFilter.encodeResponse(null, response, DEFAULT_PRODUCE_PROCESSOR, responseEx); } catch (Throwable e) { LOGGER.error("Failed to send response when prepare invocation failed, request uri:{}", requestEx.getRequestURI(), e); } - flushResponse("UNKNOWN_OPERATION"); + endResponse(null); return null; } @Override - protected void sendResponse(Invocation invocation, Response response) { - if (isDownloadFileResponseType(invocation, response)) { - responseEx.sendPart(PartUtils.getSinglePart(null, response.getResult())) - .whenComplete((r, e) -> flushResponse(invocation.getMicroserviceQualifiedName())); - return; - } + protected void endResponse(Invocation invocation, Response response) { + invocation.getInvocationStageTrace().startProviderSendResponse(); - flushResponse(invocation.getMicroserviceQualifiedName()); + endResponse(invocation); } - private void flushResponse(String operationName) { + private void endResponse(Invocation invocation) { try { - responseEx.flushBuffer(); + responseEx.endResponse(); } catch (Throwable flushException) { LOGGER.error("Failed to flush rest response, operation:{}, request uri:{}", - operationName, requestEx.getRequestURI(), flushException); + invocation == null ? "NA" : + invocation.getMicroserviceQualifiedName(), requestEx.getRequestURI(), flushException); } try { requestEx.getAsyncContext().complete(); } catch (Throwable completeException) { LOGGER.error("Failed to complete async rest response, operation:{}, request uri:{}", - operationName, requestEx.getRequestURI(), completeException); + invocation == null ? "NA" : + invocation.getMicroserviceQualifiedName(), requestEx.getRequestURI(), completeException); + } + + if (invocation != null) { + invocation.getInvocationStageTrace().finishProviderSendResponse(); } } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java index fcae4c7ab7f..83f14c64eca 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java @@ -16,8 +16,6 @@ */ package org.apache.servicecomb.common.rest; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.MicroserviceMeta; @@ -29,17 +27,16 @@ public class RestVertxProducerInvocationCreator extends RestProducerInvocationCreator { private final RoutingContext routingContext; - public RestVertxProducerInvocationCreator(@Nonnull RoutingContext routingContext, + public RestVertxProducerInvocationCreator(RoutingContext routingContext, MicroserviceMeta microserviceMeta, Endpoint endpoint, - @Nonnull HttpServletRequestEx requestEx, @Nonnull HttpServletResponseEx responseEx) { + HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { super(microserviceMeta, endpoint, requestEx, responseEx); this.routingContext = routingContext; } @Override protected void initTransportContext(Invocation invocation) { - VertxHttpTransportContext transportContext = new VertxHttpTransportContext(routingContext, requestEx, responseEx, - produceProcessor); + VertxHttpTransportContext transportContext = new VertxHttpTransportContext(routingContext, requestEx, responseEx); invocation.setTransportContext(transportContext); routingContext.put(RestConst.REST_INVOCATION_CONTEXT, invocation); } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ServerWebSocketInvocationCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ServerWebSocketInvocationCreator.java new file mode 100644 index 00000000000..46d8b705138 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ServerWebSocketInvocationCreator.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest; + +import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; +import static org.apache.servicecomb.core.exception.ExceptionCodes.NOT_DEFINED_ANY_SCHEMA; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +import org.apache.servicecomb.common.rest.locator.OperationLocator; +import org.apache.servicecomb.common.rest.locator.ServicePathManager; +import org.apache.servicecomb.config.YAMLUtil; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.core.exception.Exceptions; +import org.apache.servicecomb.core.invocation.InvocationCreator; +import org.apache.servicecomb.core.invocation.InvocationFactory; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.ServerWebSocket; +import io.vertx.core.json.Json; + +public abstract class ServerWebSocketInvocationCreator implements InvocationCreator { + private static final Logger LOGGER = LoggerFactory.getLogger(RestVertxProducerInvocationCreator.class); + + protected MicroserviceMeta microserviceMeta; + + protected final Endpoint endpoint; + + protected final ServerWebSocket websocket; + + protected RestOperationMeta restOperationMeta; + + public ServerWebSocketInvocationCreator(MicroserviceMeta microserviceMeta, Endpoint endpoint, + ServerWebSocket websocket) { + this.microserviceMeta = microserviceMeta; + this.endpoint = endpoint; + this.websocket = websocket; + } + + @Override + public CompletableFuture createAsync() { + initRestOperation(); + + Invocation invocation = createInstance(); + initInvocationContext(invocation); + addParameterContext(invocation); + initTransportContext(invocation); + + return CompletableFuture.completedFuture(invocation); + } + + protected Invocation createInstance() { + return InvocationFactory.forProvider(endpoint, restOperationMeta.getOperationMeta(), null); + } + + protected void initInvocationContext(Invocation invocation) { + if (!LegacyPropertyFactory.getBooleanProperty(RestConst.DECODE_INVOCATION_CONTEXT, true)) { + return; + } + + String strCseContext = websocket.headers().get(CoreConst.CSE_CONTEXT); + if (StringUtils.isEmpty(strCseContext)) { + return; + } + + @SuppressWarnings("unchecked") + Map invocationContext = Json.decodeValue(strCseContext, Map.class); + invocation.mergeContext(invocationContext); + } + + // No queries for websocket + protected void addParameterContext(Invocation invocation) { + String headerContextMapper = LegacyPropertyFactory + .getStringProperty(RestConst.HEADER_CONTEXT_MAPPER); + + Map headerContextMappers; + if (headerContextMapper != null) { + headerContextMappers = YAMLUtil.yaml2Properties(headerContextMapper); + } else { + headerContextMappers = new HashMap<>(); + } + + headerContextMappers.forEach((k, v) -> { + if (v instanceof String && websocket.headers().get(k) != null) { + invocation.addContext((String) v, websocket.headers().get(k)); + } + }); + } + + protected abstract void initTransportContext(Invocation invocation); + + protected void initRestOperation() { + OperationLocator locator = locateOperation(microserviceMeta); + restOperationMeta = locator.getOperation(); + } + + protected OperationLocator locateOperation(MicroserviceMeta microserviceMeta) { + ServicePathManager servicePathManager = ServicePathManager.getServicePathManager(microserviceMeta); + if (servicePathManager == null) { + LOGGER.error("No schema defined for {}:{}.", this.microserviceMeta.getAppId(), + this.microserviceMeta.getMicroserviceName()); + throw Exceptions.create(NOT_FOUND, NOT_DEFINED_ANY_SCHEMA, NOT_FOUND.getReasonPhrase()); + } + + return locateOperation(servicePathManager); + } + + protected OperationLocator locateOperation(ServicePathManager servicePathManager) { + return servicePathManager.producerLocateOperation(websocket.path(), HttpMethod.POST.name()); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/UploadConfig.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/UploadConfig.java index 302de2c8b41..ff564234124 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/UploadConfig.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/UploadConfig.java @@ -16,79 +16,51 @@ */ package org.apache.servicecomb.common.rest; -import javax.servlet.MultipartConfigElement; +import org.springframework.core.env.Environment; -import com.netflix.config.DynamicPropertyFactory; +import jakarta.servlet.MultipartConfigElement; public class UploadConfig { + private final Environment environment; + + public UploadConfig(Environment environment) { + this.environment = environment; + } /** * null means not support upload */ - private String location; + public String getLocation() { + return environment.getProperty(RestConst.UPLOAD_DIR, RestConst.UPLOAD_DEFAULT_DIR); + } /** * limit of one upload file, only available for servlet rest transport */ - private long maxFileSize; + public long getMaxFileSize() { + return environment.getProperty(RestConst.UPLOAD_MAX_FILE_SIZE, long.class, -1L); + } /** * limit of upload request body */ - private long maxSize; - - /** - * the size threshold after which files will be written to disk, only available for servlet rest transport - */ - private int fileSizeThreshold; - - public UploadConfig() { - location = DynamicPropertyFactory.getInstance() - .getStringProperty(RestConst.UPLOAD_DIR, RestConst.UPLOAD_DEFAULT_DIR).get(); - maxFileSize = DynamicPropertyFactory.getInstance().getLongProperty(RestConst.UPLOAD_MAX_FILE_SIZE, -1L).get(); - maxSize = DynamicPropertyFactory.getInstance().getLongProperty(RestConst.UPLOAD_MAX_SIZE, -1L).get(); - fileSizeThreshold = DynamicPropertyFactory.getInstance().getIntProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, 0) - .get(); - } - - public String getLocation() { - return location; - } - - public void setLocation(String location) { - this.location = location; - } - - public long getMaxFileSize() { - return maxFileSize; - } - - public void setMaxFileSize(long maxFileSize) { - this.maxFileSize = maxFileSize; - } - public long getMaxSize() { - return maxSize; + return environment.getProperty(RestConst.UPLOAD_MAX_SIZE, long.class, -1L); } - public void setMaxSize(long maxSize) { - this.maxSize = maxSize; - } + /** + * the size threshold after which files will be written to disk, only available for servlet rest transport + */ public int getFileSizeThreshold() { - return fileSizeThreshold; - } - - public void setFileSizeThreshold(int fileSizeThreshold) { - this.fileSizeThreshold = fileSizeThreshold; + return environment.getProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, int.class, 0); } public MultipartConfigElement toMultipartConfigElement() { - return new MultipartConfigElement( - location, - DynamicPropertyFactory.getInstance().getLongProperty(RestConst.UPLOAD_MAX_FILE_SIZE, -1L).get(), - DynamicPropertyFactory.getInstance().getLongProperty(RestConst.UPLOAD_MAX_SIZE, -1L).get(), - DynamicPropertyFactory.getInstance().getIntProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, 0).get()); + getLocation(), + getMaxFileSize(), + getMaxSize(), + getFileSizeThreshold()); } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/VertxHttpTransportContext.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/VertxHttpTransportContext.java index b88c1a12b41..55576022edd 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/VertxHttpTransportContext.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/VertxHttpTransportContext.java @@ -16,7 +16,6 @@ */ package org.apache.servicecomb.common.rest; -import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.context.VertxTransportContext; @@ -31,8 +30,8 @@ public class VertxHttpTransportContext extends HttpTransportContext implements V private final Context vertxContext; public VertxHttpTransportContext(RoutingContext routingContext, HttpServletRequestEx requestEx, - HttpServletResponseEx responseEx, ProduceProcessor produceProcessor) { - super(requestEx, responseEx, produceProcessor); + HttpServletResponseEx responseEx) { + super(requestEx, responseEx); this.routingContext = routingContext; this.vertxContext = Vertx.currentContext(); diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/WebSocketTransportContext.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/WebSocketTransportContext.java new file mode 100644 index 00000000000..738b09788ff --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/WebSocketTransportContext.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.servicecomb.common.rest; + +import org.apache.servicecomb.swagger.invocation.context.TransportContext; + +import io.vertx.core.http.ServerWebSocket; + +public class WebSocketTransportContext implements TransportContext { + private final ServerWebSocket serverWebSocket; + + public WebSocketTransportContext(ServerWebSocket serverWebSocket) { + this.serverWebSocket = serverWebSocket; + } + + public ServerWebSocket getServerWebSocket() { + return this.serverWebSocket; + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java index 62d768847e6..74967694a90 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java @@ -20,6 +20,7 @@ import io.vertx.core.Future; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClientRequest; /** * vertx的HttpClientRequest没有getHeader的能力 @@ -41,4 +42,6 @@ public interface RestClientRequest { Buffer getBodyBuffer() throws Exception; void attach(String name, Object partOrList); + + HttpClientRequest getHttpClientRequest(); } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestCodec.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestCodec.java index bf5d83ddb3e..260ac0d4cb1 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestCodec.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestCodec.java @@ -21,21 +21,15 @@ import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.Response.Status; - -import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.definition.RestParam; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.netflix.config.DynamicPropertyFactory; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Response.Status; public final class RestCodec { - private static final Logger LOG = LoggerFactory.getLogger(RestCodec.class); - private RestCodec() { } @@ -60,22 +54,14 @@ public static Map restToArgs(HttpServletRequest request, for (RestParam param : paramList) { try { paramValues.put(param.getParamName(), param.getParamProcessor().getValue(request)); - } catch (InvocationException e) { - throw e; } catch (Exception e) { - // Avoid information leak of user input, and add option for debug use. String message = String - .format("Parameter is not valid for operation [%s]. Parameter is [%s]. Processor is [%s].", + .format("Parameter is not valid for operation [%s]. Parameter is [%s]. Processor is [%s]. Message is [%s].", restOperation.getOperationMeta().getMicroserviceQualifiedName(), param.getParamName(), - param.getParamProcessor().getProcessorType()); - if (DynamicPropertyFactory.getInstance().getBooleanProperty( - RestConst.PRINT_CODEC_ERROR_MESSGAGE, false).get()) { - LOG.error(message, e); - } else { - LOG.error("{} Add {}=true to print the details.", message, RestConst.PRINT_CODEC_ERROR_MESSGAGE); - } - throw new InvocationException(Status.BAD_REQUEST, message); + param.getParamProcessor().getProcessorType(), + e.getMessage()); + throw new InvocationException(Status.BAD_REQUEST, new CommonExceptionData(message), e); } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodec.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodec.java new file mode 100644 index 00000000000..6b6e06f41e1 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodec.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.servicecomb.common.rest.codec.header; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +import org.apache.servicecomb.common.rest.codec.RestClientRequest; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; + +import jakarta.servlet.http.HttpServletRequest; + +public interface HeaderCodec { + static String encodeValue(Object value) throws UnsupportedEncodingException { + return URLEncoder.encode(value.toString(), StandardCharsets.UTF_8.name()); + } + + // can not be replaced by value.toString() because of date serialize + static String convertToString(Object value) throws Exception { + return RestObjectMapperFactory.getRestObjectMapper().convertToString(value); + } + + String getCodecName(); + + void encode(RestClientRequest clientRequest, String name, Object value) throws Exception; + + Object decode(HeaderProcessor processor, HttpServletRequest request); +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecCsv.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecCsv.java new file mode 100644 index 00000000000..26d86bbf2b7 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecCsv.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.header; + +public class HeaderCodecCsv extends HeaderCodecWithDelimiter { + public static final String CODEC_NAME = "form:0"; + + public static final String DELIMITER = ","; + + public HeaderCodecCsv() { + super(CODEC_NAME, DELIMITER, DELIMITER); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecMulti.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecMulti.java new file mode 100644 index 00000000000..7a61e0fa125 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecMulti.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.header; + +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; + +import org.apache.servicecomb.common.rest.codec.RestClientRequest; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Response.Status; + +public class HeaderCodecMulti implements HeaderCodec { + public static final String NAME = "form:1"; + + @Override + public String getCodecName() { + return NAME; + } + + @Override + public void encode(RestClientRequest clientRequest, String name, Object value) throws Exception { + if (null == value) { + // if value is empty, header should not be set to clientRequest to avoid NullPointerException in Netty. + return; + } + if (!(value instanceof Collection)) { + throw new InvocationException(Status.BAD_REQUEST, + new CommonExceptionData("Array type of header should be Collection")); + } + for (Object item : ((Collection) value)) { + clientRequest.putHeader(name, + RestObjectMapperFactory.getConsumerWriterMapper().convertToString(item)); + } + } + + @Override + public Object decode(HeaderProcessor processor, HttpServletRequest request) { + Enumeration headerValues = request.getHeaders(processor.getParameterPath()); + if (headerValues == null) { + //Even if the paramPath does not exist, headerValues won't be null at now + return null; + } + return processor.convertValue(Collections.list(headerValues), processor.getTargetType()); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecPipes.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecPipes.java new file mode 100644 index 00000000000..f458946ba61 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecPipes.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.servicecomb.common.rest.codec.header; + +public class HeaderCodecPipes extends HeaderCodecWithDelimiter { + public static final String CODEC_NAME = "pipeDelimited:0"; + + public static final String JOIN_DELIMITER = "|"; + + public static final String SPLIT_DELIMITER = "\\|"; + + public HeaderCodecPipes() { + super(CODEC_NAME, JOIN_DELIMITER, SPLIT_DELIMITER); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSimple.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSimple.java new file mode 100644 index 00000000000..0dd46a86b90 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSimple.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.servicecomb.common.rest.codec.header; + +import org.apache.servicecomb.common.rest.codec.RestClientRequest; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; + +import jakarta.servlet.http.HttpServletRequest; + +public class HeaderCodecSimple implements HeaderCodec { + public static final String NAME = "simple"; + + @Override + public String getCodecName() { + return NAME; + } + + @Override + public void encode(RestClientRequest clientRequest, String name, Object value) throws Exception { + if (null == value) { + // if value is empty, header should not be set to clientRequest to avoid NullPointerException in Netty. + return; + } + clientRequest.putHeader(name, + RestObjectMapperFactory.getConsumerWriterMapper().convertToString(value)); + } + + @Override + public Object decode(HeaderProcessor processor, HttpServletRequest request) { + Object value = request.getHeader(processor.getParameterPath()); + if (value == null) { + value = processor.checkRequiredAndDefaultValue(); + } + return processor.convertValue(value, processor.getTargetType()); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSsv.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSsv.java new file mode 100644 index 00000000000..a9266f043ef --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSsv.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.header; + +public class HeaderCodecSsv extends HeaderCodecWithDelimiter { + public static final String CODEC_NAME = "spaceDelimited:0"; + + public static final String DELIMITER = " "; + + public HeaderCodecSsv() { + super(CODEC_NAME, DELIMITER, DELIMITER); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecWithDelimiter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecWithDelimiter.java new file mode 100644 index 00000000000..c85742b5549 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecWithDelimiter.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.header; + +import java.util.Collection; +import java.util.List; +import java.util.StringJoiner; + +import org.apache.servicecomb.common.rest.codec.RestClientRequest; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Response.Status; + +public abstract class HeaderCodecWithDelimiter implements HeaderCodec { + private final String name; + + private final String joinDelimiter; + + private final String splitDelimiter; + + public HeaderCodecWithDelimiter(String name, String joinDelimiter, String splitDelimiter) { + this.name = name; + this.joinDelimiter = joinDelimiter; + this.splitDelimiter = splitDelimiter; + } + + + @Override + public String getCodecName() { + return name; + } + + @Override + public void encode(RestClientRequest clientRequest, String name, Object value) throws Exception { + if (null == value) { + // if value is empty, header should not be set to clientRequest to avoid NullPointerException in Netty. + return; + } + if (!(value instanceof Collection)) { + throw new InvocationException(Status.BAD_REQUEST, + new CommonExceptionData("Array type of header should be Collection")); + } + clientRequest.putHeader(name, join((Collection) value)); + } + + protected String join(Collection values) throws Exception { + StringJoiner joiner = new StringJoiner(joinDelimiter); + for (Object value : values) { + String strValue = RestObjectMapperFactory.getConsumerWriterMapper().convertToString(value); + joiner.add(strValue); + } + + return joiner.toString(); + } + + @Override + public Object decode(HeaderProcessor processor, HttpServletRequest request) { + String headerValues = request.getHeader(processor.getParameterPath()); + if (headerValues == null) { + headerValues = (String) processor.checkRequiredAndDefaultValue(); + } + + return processor.convertValue(List.of(headerValues.split(splitDelimiter)), processor.getTargetType()); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecsUtils.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecsUtils.java new file mode 100644 index 00000000000..fb9509a1f90 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecsUtils.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.servicecomb.common.rest.codec.header; + +import java.util.HashMap; +import java.util.Map; + +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; + +public class HeaderCodecsUtils { + private static final Map CODECS; + + static { + CODECS = new HashMap<>(); + CODECS.put(HeaderCodecSimple.NAME, new HeaderCodecSimple()); + CODECS.put(HeaderCodecMulti.NAME, new HeaderCodecMulti()); + CODECS.put(HeaderCodecCsv.CODEC_NAME, new HeaderCodecCsv()); + CODECS.put(HeaderCodecPipes.CODEC_NAME, new HeaderCodecPipes()); + CODECS.put(HeaderCodecSsv.CODEC_NAME, new HeaderCodecSsv()); + } + + private HeaderCodecsUtils() { + } + + public static HeaderCodec find(Parameter.StyleEnum styleEnum, Boolean explode) { + return CODECS.get(formatName(styleEnum, explode)); + } + + private static String formatName(StyleEnum styleEnum, Boolean explode) { + if (styleEnum == null) { + return HeaderCodecSimple.NAME; + } + return styleEnum + ":" + + (explode != null && explode ? "1" : "0"); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java index d05b2a91e16..e789038c467 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java @@ -21,21 +21,27 @@ import java.io.InputStream; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; -import java.util.Locale; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response.Status; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.http.entity.ContentType; +import org.apache.servicecomb.codec.protobuf.utils.ScopedProtobufSchemaManager; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.RootSerializer; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; import org.apache.servicecomb.swagger.SwaggerUtils; -import org.apache.servicecomb.swagger.converter.ConverterMgr; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; @@ -46,51 +52,99 @@ import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.type.SimpleType; import com.fasterxml.jackson.databind.type.TypeFactory; -import com.netflix.config.DynamicPropertyFactory; -import io.swagger.models.Model; -import io.swagger.models.ModelImpl; -import io.swagger.models.parameters.BodyParameter; -import io.swagger.models.parameters.Parameter; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.parameters.RequestBody; import io.vertx.core.buffer.Buffer; import io.vertx.core.buffer.impl.BufferImpl; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response.Status; + +public class BodyProcessorCreator implements ParamValueProcessorCreator { -public class BodyProcessorCreator implements ParamValueProcessorCreator { private static final Logger LOGGER = LoggerFactory.getLogger(BodyProcessorCreator.class); + public static final String REQUEST_BODY_NAME = "X_REQUEST"; + + public static final String EXT_ID = "protobuf"; + public static final String PARAM_TYPE = "body"; private static final JavaType OBJECT_TYPE = SimpleType.constructUnsafe(Object.class); - // This configuration is used for temporary use only. Do not use it if you are sure how it works. And may be deleted in future. - private static final boolean decodeAsObject = DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.rest.parameter.decodeAsObject", false).get(); + private static final Object LOCK = new Object(); + + // This configuration is used for temporary use only. + // Do not use it if you are sure how it works. And may be deleted in the future. + public static final String PARAM_DECODE_AS_OBJECT = "servicecomb.rest.parameter.decodeAsObject"; + + public static final String PARAM_DEFAULT_REQUEST_ENCODING = "servicecomb.rest.parameter.default-request-encoding"; + + private static Boolean decodeAsObject; + + private static String defaultRequestEncoding; public static class BodyProcessor implements ParamValueProcessor { + // Producer target type. For consumer, is null. protected JavaType targetType; protected Class serialViewClass; - private final boolean isString; - protected boolean isRequired; - public BodyProcessor(JavaType targetType, boolean isString, boolean isRequired) { - this(targetType, null, isString, isRequired); - } + protected OpenAPI openAPI; - public BodyProcessor(JavaType targetType, String serialViewClass, boolean isString, boolean isRequired) { - if (!StringUtils.isEmpty(serialViewClass)) { + protected ScopedProtobufSchemaManager scopedProtobufSchemaManager; + + protected List supportedContentTypes = new ArrayList<>(); + + protected OperationMeta operationMeta; + + protected RequestBody requestBody; + + public BodyProcessor(OperationMeta operationMeta, JavaType targetType, RequestBody requestBody) { + this.requestBody = requestBody; + if (!StringUtils.isEmpty((String) this.requestBody.getExtensions() + .get(SwaggerConst.EXT_JSON_VIEW))) { try { - this.serialViewClass = Class.forName(serialViewClass); + this.serialViewClass = Class.forName((String) this.requestBody.getExtensions() + .get(SwaggerConst.EXT_JSON_VIEW)); } catch (Throwable e) { //ignore LOGGER.warn("Failed to create body processor {}, annotation @JsonView may be invalid", serialViewClass, e); } } + this.targetType = targetType; - this.isString = isString; - this.isRequired = isRequired; + this.isRequired = this.requestBody.getRequired() != null && this.requestBody.getRequired(); + if (this.requestBody.getContent() != null) { + supportedContentTypes.addAll(this.requestBody.getContent().keySet()); + } + + if (operationMeta != null) { + this.operationMeta = operationMeta; + this.openAPI = operationMeta.getSchemaMeta().getSwagger(); + if (supportedContentTypes.contains(SwaggerConst.PROTOBUF_TYPE)) { + this.scopedProtobufSchemaManager = getOrCreateScopedProtobufSchemaManager( + operationMeta.getMicroserviceMeta()); + } + } + } + + private ScopedProtobufSchemaManager getOrCreateScopedProtobufSchemaManager(MicroserviceMeta microserviceMeta) { + ScopedProtobufSchemaManager scopedProtobufSchemaManager = microserviceMeta.getExtData(EXT_ID); + if (scopedProtobufSchemaManager == null) { + synchronized (LOCK) { + scopedProtobufSchemaManager = microserviceMeta.getExtData(EXT_ID); + if (scopedProtobufSchemaManager == null) { + scopedProtobufSchemaManager = new ScopedProtobufSchemaManager(); + microserviceMeta.putExtData(EXT_ID, scopedProtobufSchemaManager); + } + } + } + return scopedProtobufSchemaManager; } @Override @@ -114,10 +168,11 @@ private Object getValueImpl(HttpServletRequest request) throws IOException { } // edge support convert from form-data or x-www-form-urlencoded to json automatically - String contentType = request.getContentType(); - contentType = contentType == null ? "" : contentType.toLowerCase(Locale.US); - if (contentType.startsWith(MediaType.MULTIPART_FORM_DATA) - || contentType.startsWith(MediaType.APPLICATION_FORM_URLENCODED)) { + String contentType = validContentType(request.getContentType()); + + // support RFC 7231, ignore case for content-type + if (StringUtils.equalsIgnoreCase(contentType, MediaType.MULTIPART_FORM_DATA) + || StringUtils.equalsIgnoreCase(contentType, MediaType.APPLICATION_FORM_URLENCODED)) { return convertValue(request.getParameterMap(), targetType); } @@ -129,67 +184,124 @@ private Object getValueImpl(HttpServletRequest request) throws IOException { return null; } - if (!contentType.isEmpty() && !contentType.startsWith(MediaType.APPLICATION_JSON)) { - // TODO: we should consider body encoding - return IOUtils.toString(inputStream, StandardCharsets.UTF_8); + if (StringUtils.equalsIgnoreCase(contentType, MediaType.APPLICATION_JSON)) { + try { + ObjectReader reader = serialViewClass != null + ? RestObjectMapperFactory.getRestObjectMapper().readerWithView(serialViewClass) + : RestObjectMapperFactory.getRestObjectMapper().reader(); + if (decodeAsObject()) { + return reader.forType(OBJECT_TYPE).readValue(inputStream); + } + return reader.forType(targetType == null ? OBJECT_TYPE : targetType) + .readValue(inputStream); + } catch (MismatchedInputException e) { + // there is no way to detect InputStream is empty, so have to catch the exception + if (!isRequired && e.getMessage().contains("No content to map due to end-of-input")) { + LOGGER.info("Empty content and required is false, taken as null"); + return null; + } + throw e; + } + } + + if (StringUtils.equalsIgnoreCase(contentType, SwaggerConst.PROTOBUF_TYPE)) { + ProtoMapper protoMapper = scopedProtobufSchemaManager + .getOrCreateProtoMapper(openAPI, operationMeta.getSchemaId(), + REQUEST_BODY_NAME, + requestBody.getContent().get(SwaggerConst.PROTOBUF_TYPE).getSchema()); + RootDeserializer> deserializer = protoMapper.getDeserializerSchemaManager() + .createRootDeserializer(protoMapper.getProto().getMessage(REQUEST_BODY_NAME), + targetType == null ? OBJECT_TYPE : targetType); + PropertyWrapper result = deserializer.deserialize(inputStream.readAllBytes()); + return result.getValue(); } - try { - ObjectReader reader = serialViewClass != null - ? RestObjectMapperFactory.getRestObjectMapper().readerWithView(serialViewClass) - : RestObjectMapperFactory.getRestObjectMapper().reader(); - if (decodeAsObject) { - return reader.forType(OBJECT_TYPE).readValue(inputStream); + + if (StringUtils.equalsIgnoreCase(contentType, MediaType.TEXT_PLAIN)) { + try { + if (targetType != null && String.class.equals(targetType.getRawClass())) { + return IOUtils.toString(inputStream, StandardCharsets.UTF_8); + } + ObjectReader reader = serialViewClass != null + ? RestObjectMapperFactory.getRestObjectMapper().readerWithView(serialViewClass) + : RestObjectMapperFactory.getRestObjectMapper().reader(); + if (decodeAsObject()) { + return reader.forType(OBJECT_TYPE).readValue(inputStream); + } + return reader.forType(targetType == null ? OBJECT_TYPE : targetType) + .readValue(inputStream); + } catch (MismatchedInputException e) { + // there is no way to detect InputStream is empty, so have to catch the exception + if (!isRequired && e.getMessage().contains("No content to map due to end-of-input")) { + LOGGER.info("Empty content and required is false, taken as null"); + return null; + } + throw e; + } + } + + throw new IllegalArgumentException(String.format("operation %s not support content-type %s", + operationMeta.getSchemaQualifiedName(), contentType)); + } + + private String validContentType(String type) { + if (StringUtils.isEmpty(type)) { + if (supportedContentTypes.size() == 0) { + throw new IllegalArgumentException("operation do not have any content type support."); } - return reader.forType(targetType == null ? OBJECT_TYPE : targetType) - .readValue(inputStream); - } catch (MismatchedInputException e) { - // there is no way to detect InputStream is empty, so have to catch the exception - if (!isRequired && e.getMessage().contains("No content to map due to end-of-input")) { - LOGGER.info("Empty content and required is false, taken as null"); - return null; + if (supportedContentTypes.contains(clientEncodingDefault())) { + return clientEncodingDefault(); } - throw e; + return supportedContentTypes.get(0); } + ContentType contentType = ContentType.parse(type); + return contentType.getMimeType(); } @Override public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { - ensureContentType(clientRequest); + String userContentType = clientRequest.getHeaders().get(HttpHeaders.CONTENT_TYPE); + String contentType = validContentType(userContentType); + if (StringUtils.isEmpty(userContentType)) { + clientRequest.putHeader(HttpHeaders.CONTENT_TYPE, contentType); + } if (arg != null) { - Buffer buffer = createBodyBuffer(clientRequest.getHeaders().get(HttpHeaders.CONTENT_TYPE), arg); + Buffer buffer = createBodyBuffer(contentType, arg); clientRequest.write(buffer); } } /** - * Deserialize body object into body buffer, according to the Content-Type. - * - * @param contentType the Content-Type of request - * @param arg body param object - * @return the deserialized body buffer - * @throws IOException + * Serialize body object into body buffer, according to the Content-Type. */ private Buffer createBodyBuffer(String contentType, Object arg) throws IOException { - if (MediaType.TEXT_PLAIN.equals(contentType)) { - if (!(arg instanceof String)) { - throw new IllegalArgumentException("Content-Type is text/plain while arg type is not String"); + if (MediaType.APPLICATION_JSON.equals(contentType)) { + try (BufferOutputStream output = new BufferOutputStream()) { + RestObjectMapperFactory.getConsumerWriterMapper().writeValue(output, arg); + return output.getBuffer(); } - return new BufferImpl().appendBytes(((String) arg).getBytes(StandardCharsets.UTF_8)); } - try (BufferOutputStream output = new BufferOutputStream()) { - RestObjectMapperFactory.getConsumerWriterMapper().writeValue(output, arg); - return output.getBuffer(); + if (SwaggerConst.PROTOBUF_TYPE.equals(contentType)) { + ProtoMapper protoMapper = scopedProtobufSchemaManager + .getOrCreateProtoMapper(openAPI, operationMeta.getSchemaId(), + REQUEST_BODY_NAME, + requestBody.getContent().get(SwaggerConst.PROTOBUF_TYPE).getSchema()); + RootSerializer serializer = protoMapper.getSerializerSchemaManager() + .createRootSerializer(protoMapper.getProto().getMessage(REQUEST_BODY_NAME), + Object.class); + Map bodyArg = new HashMap<>(1); + bodyArg.put("value", arg); + return new BufferImpl().appendBytes(serializer.serialize(bodyArg)); } - } - /** - * If the Content-Type has not been set yet, set application/json as default value. - */ - private void ensureContentType(RestClientRequest clientRequest) { - if (null == clientRequest.getHeaders() - || StringUtils.isEmpty(clientRequest.getHeaders().get(HttpHeaders.CONTENT_TYPE))) { - clientRequest.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); + // For text/plain + try (BufferOutputStream output = new BufferOutputStream()) { + if (arg instanceof String) { + output.write(((String) arg).getBytes(StandardCharsets.UTF_8)); + } else { + RestObjectMapperFactory.getConsumerWriterMapper().writeValue(output, arg); + } + return output.getBuffer(); } } @@ -204,13 +316,28 @@ public String getProcessorType() { } } - public static class RawJsonBodyProcessor extends BodyProcessor { - public RawJsonBodyProcessor(JavaType targetType, boolean isString, boolean isRequired) { - this(targetType, null, isString, isRequired); + public static class RawJsonBodyProcessor implements ParamValueProcessor { + protected JavaType targetType; + + protected Class serialViewClass; + + protected boolean isRequired; + + public RawJsonBodyProcessor(JavaType targetType, boolean isRequired) { + this(targetType, null, isRequired); } - public RawJsonBodyProcessor(JavaType targetType, String serialViewClass, boolean isString, boolean isRequired) { - super(targetType, serialViewClass, isString, isRequired); + public RawJsonBodyProcessor(JavaType targetType, String serialViewClass, boolean isRequired) { + if (!StringUtils.isEmpty(serialViewClass)) { + try { + this.serialViewClass = Class.forName(serialViewClass); + } catch (Throwable e) { + //ignore + LOGGER.warn("Failed to create body processor {}, annotation @JsonView may be invalid", serialViewClass, e); + } + } + this.targetType = targetType; + this.isRequired = isRequired; } @Override @@ -225,7 +352,6 @@ public Object getValue(HttpServletRequest request) throws Exception { return null; } - // TODO: we should consider body encoding return IOUtils.toString(inputStream, StandardCharsets.UTF_8); } @@ -237,7 +363,17 @@ public void setValue(RestClientRequest clientRequest, Object arg) throws Excepti return; } - super.setValue(clientRequest, arg); + throw new IllegalArgumentException("@RawJsonRequestBody only supports string type."); + } + + @Override + public String getParameterPath() { + return ""; + } + + @Override + public String getProcessorType() { + return PARAM_TYPE; } } @@ -246,23 +382,33 @@ public BodyProcessorCreator() { } @Override - public ParamValueProcessor create(Parameter parameter, Type genericParamType) { - Model model = ((BodyParameter) parameter).getSchema(); - JavaType swaggerType = null; - if (model instanceof ModelImpl) { - swaggerType = ConverterMgr.findJavaType(((ModelImpl) model).getType(), ((ModelImpl) model).getFormat()); - } - boolean isString = swaggerType != null && swaggerType.getRawClass().equals(String.class); - + public ParamValueProcessor create(OperationMeta operationMeta, String parameterName, + RequestBody parameter, Type genericParamType) { JavaType targetType = genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); boolean rawJson = SwaggerUtils.isRawJsonType(parameter); if (rawJson) { - return new RawJsonBodyProcessor(targetType, (String) parameter.getVendorExtensions() - .get(SwaggerConst.EXT_JSON_VIEW), isString, parameter.getRequired()); + return new RawJsonBodyProcessor(targetType, (String) parameter.getExtensions() + .get(SwaggerConst.EXT_JSON_VIEW), + parameter.getRequired() != null && parameter.getRequired()); + } + + return new BodyProcessor(operationMeta, targetType, parameter); + } + + private static boolean decodeAsObject() { + if (decodeAsObject == null) { + decodeAsObject = LegacyPropertyFactory + .getBooleanProperty(PARAM_DECODE_AS_OBJECT, false); } + return decodeAsObject; + } - return new BodyProcessor(targetType, (String) parameter.getVendorExtensions() - .get(SwaggerConst.EXT_JSON_VIEW), isString, parameter.getRequired()); + private static String clientEncodingDefault() { + if (defaultRequestEncoding == null) { + defaultRequestEncoding = LegacyPropertyFactory + .getStringProperty(PARAM_DEFAULT_REQUEST_ENCODING, MediaType.APPLICATION_JSON); + } + return defaultRequestEncoding; } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/CookieProcessorCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/CookieProcessorCreator.java index a21c23719e5..9426cc2e232 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/CookieProcessorCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/CookieProcessorCreator.java @@ -20,21 +20,20 @@ import java.lang.reflect.Type; import java.util.Objects; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.Response.Status; - import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; -import io.swagger.models.parameters.CookieParameter; -import io.swagger.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.Parameter; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Response.Status; -public class CookieProcessorCreator implements ParamValueProcessorCreator { +public class CookieProcessorCreator implements ParamValueProcessorCreator { public static final String PARAMTYPE = "cookie"; public static class CookieProcessor extends AbstractParamProcessor { @@ -87,10 +86,11 @@ public CookieProcessorCreator() { } @Override - public ParamValueProcessor create(Parameter parameter, Type genericParamType) { + public ParamValueProcessor create(OperationMeta operationMeta, + String parameterName, Parameter parameter, Type genericParamType) { JavaType targetType = genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); - return new CookieProcessor(parameter.getName(), targetType, ((CookieParameter) parameter).getDefaultValue(), - parameter.getRequired()); + return new CookieProcessor(parameterName, targetType, parameter.getSchema().getDefault(), + parameter.getRequired() != null && parameter.getRequired()); } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java index de5ad07e4cc..740f57e039f 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java @@ -23,42 +23,45 @@ import java.util.function.Function; import java.util.stream.Collectors; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.Part; -import javax.ws.rs.core.Response.Status; - import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.invocation.converter.Converter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; -import com.google.inject.util.Types; -import io.swagger.models.parameters.FormParameter; -import io.swagger.models.parameters.Parameter; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.FileProperty; -import io.swagger.models.properties.Property; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.RequestBody; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.Response.Status; -public class FormProcessorCreator implements ParamValueProcessorCreator { +@SuppressWarnings("unchecked") +public class FormProcessorCreator implements ParamValueProcessorCreator { public static final String PARAMTYPE = "formData"; public static class FormProcessor extends AbstractParamProcessor { private final boolean repeatedType; - public FormProcessor(FormParameter formParameter, JavaType targetType) { - super(formParameter.getName(), targetType, formParameter.getDefaultValue(), formParameter.getRequired()); + public FormProcessor(String paraName, RequestBody formParameter, String mediaType, JavaType targetType) { + super(paraName, targetType, + formParameter.getContent().get(mediaType).getSchema().getDefault(), + formParameter.getRequired() != null && formParameter.getRequired()); - this.repeatedType = ArrayProperty.isType(formParameter.getType()); + this.repeatedType = formParameter.getContent().get(mediaType) + .getSchema().getProperties().get(paraName) instanceof ArraySchema; } @Override public Object getValue(HttpServletRequest request) { - @SuppressWarnings("unchecked") - Map forms = (Map) request.getAttribute(RestConst.FORM_PARAMETERS); + Map forms = (Map) request.getAttribute(RestConst.BODY_PARAMETER); if (forms != null && !forms.isEmpty()) { return convertValue(forms.get(paramPath), targetType); } @@ -98,31 +101,41 @@ public FormProcessorCreator() { } @Override - public ParamValueProcessor create(Parameter parameter, Type genericParamType) { + public ParamValueProcessor create(OperationMeta operationMeta, + String paramName, RequestBody parameter, Type genericParamType) { JavaType targetType = genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); - if (isPart(parameter)) { - return new PartProcessor((FormParameter) parameter, genericParamType); + if (isPart(parameter, paramName)) { + return new PartProcessor(paramName, parameter, genericParamType); + } + String mediaType = SwaggerConst.FORM_MEDIA_TYPE; + if (parameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE) != null) { + mediaType = SwaggerConst.FILE_MEDIA_TYPE; } - return new FormProcessor((FormParameter) parameter, targetType); + return new FormProcessor(paramName, parameter, mediaType, targetType); } - private boolean isPart(Parameter parameter) { - // no need to check Part[][] and so on - FormParameter formParameter = (FormParameter) parameter; - if ("array".equals(formParameter.getType())) { - Property items = formParameter.getItems(); - return new FileProperty().getType().equals(items.getType()); + private boolean isPart(RequestBody parameter, String paramName) { + MediaType file = parameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE); + if (file != null) { + Schema schema = (Schema) file.getSchema().getProperties().get(paramName); + if (schema instanceof ArraySchema) { + return "string".equals(schema.getItems().getType()) && + "binary".equals(schema.getItems().getFormat()); + } else { + return ("string".equals(schema.getType()) && "binary".equals(schema.getFormat())); + } } - return new FileProperty().getType().equals(formParameter.getType()); + return false; } public static class PartProcessor extends AbstractParamProcessor { - private static final Type partListType = Types.newParameterizedType(List.class, Part.class); + private static final Type partListType = ParameterizedTypeUtil.make(List.class, Part.class); // key is target type - private static final Map partsToTargetConverters = SPIServiceUtils.getSortedService(Converter.class) + private static final Map partsToTargetConverters = SPIServiceUtils.getSortedService( + Converter.class) .stream() .filter(c -> partListType.equals(c.getSrcType())) .collect(Collectors.toMap(Converter::getTargetType, Function.identity())); @@ -135,15 +148,15 @@ public static class PartProcessor extends AbstractParamProcessor { private final boolean repeatedType; - private final Type genericParamType; - private Converter converter; - PartProcessor(FormParameter formParameter, Type genericParamType) { - super(formParameter.getName(), null, formParameter.getDefaultValue(), formParameter.getRequired()); + PartProcessor(String paramName, RequestBody formParameter, Type genericParamType) { + super(paramName, null, + formParameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE).getSchema().getDefault(), + formParameter.getRequired() != null && formParameter.getRequired()); - this.genericParamType = genericParamType; - this.repeatedType = ArrayProperty.isType(formParameter.getType()); + this.repeatedType = formParameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE) + .getSchema().getProperties().get(paramName) instanceof ArraySchema; initConverter(genericParamType); } @@ -165,7 +178,7 @@ private void initNormalConverter(Type genericParamType) { private void initRepeatedConverter(Type genericParamType) { if (genericParamType instanceof JavaType) { - genericParamType = Types.newParameterizedType(((JavaType) genericParamType).getRawClass(), + genericParamType = ParameterizedTypeUtil.make(((JavaType) genericParamType).getRawClass(), ((JavaType) genericParamType).getContentType()); } converter = partsToTargetConverters.get(genericParamType); diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java index cb40fdf641b..3bfb2675046 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java @@ -18,63 +18,52 @@ package org.apache.servicecomb.common.rest.codec.param; import java.lang.reflect.Type; -import java.util.Collections; -import java.util.Enumeration; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.Response.Status; import org.apache.servicecomb.common.rest.codec.RestClientRequest; -import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.common.rest.codec.header.HeaderCodec; +import org.apache.servicecomb.common.rest.codec.header.HeaderCodecsUtils; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; -import com.netflix.config.DynamicPropertyFactory; - -import io.swagger.models.parameters.HeaderParameter; -import io.swagger.models.parameters.Parameter; -import io.swagger.models.properties.ArrayProperty; -public class HeaderProcessorCreator implements ParamValueProcessorCreator { - private static final Logger LOGGER = LoggerFactory.getLogger(HeaderProcessorCreator.class); +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.parameters.HeaderParameter; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Response.Status; +public class HeaderProcessorCreator implements ParamValueProcessorCreator { public static final String PARAMTYPE = "header"; public static class HeaderProcessor extends AbstractParamProcessor { // This configuration is used for temporary use only. Do not use it if you are sure how it works. And may be deleted in future. - private final boolean ignoreRequiredCheck = DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.rest.parameter.header.ignoreRequiredCheck", false).get(); + private final boolean ignoreRequiredCheck = LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.parameter.header.ignoreRequiredCheck", false); - private final boolean repeatedType; + private final HeaderCodec headerCodec; public HeaderProcessor(HeaderParameter headerParameter, JavaType targetType) { - super(headerParameter.getName(), targetType, headerParameter.getDefaultValue(), headerParameter.getRequired()); - - this.repeatedType = ArrayProperty.isType(headerParameter.getType()); + super(headerParameter.getName(), targetType, headerParameter.getSchema().getDefault(), + headerParameter.getRequired() != null && headerParameter.getRequired()); + + if ((headerParameter.getSchema() instanceof ArraySchema) && headerParameter.getStyle() == null) { + // compatible to default settings + this.headerCodec = HeaderCodecsUtils.find(StyleEnum.FORM, true); + } else { + this.headerCodec = HeaderCodecsUtils.find(headerParameter.getStyle(), headerParameter.getExplode()); + } } @Override public Object getValue(HttpServletRequest request) { - if (repeatedType) { - Enumeration headerValues = request.getHeaders(paramPath); - if (headerValues == null) { - //Even if the paramPath does not exist, headerValues won't be null at now - return null; - } - return convertValue(Collections.list(headerValues), targetType); - } - - Object value = request.getHeader(paramPath); - if (value == null) { - value = checkRequiredAndDefaultValue(); - } - return convertValue(value, targetType); + return headerCodec.decode(this, request); } - private Object checkRequiredAndDefaultValue() { + public Object checkRequiredAndDefaultValue() { if (!ignoreRequiredCheck && isRequired()) { throw new InvocationException(Status.BAD_REQUEST, "Parameter is required."); } @@ -83,13 +72,7 @@ private Object checkRequiredAndDefaultValue() { @Override public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { - if (null == arg) { - // null header should not be set to clientRequest to avoid NullPointerException in Netty. - LOGGER.debug("Header arg is null, will not be set into clientRequest. paramPath = [{}]", paramPath); - return; - } - clientRequest.putHeader(paramPath, - RestObjectMapperFactory.getConsumerWriterMapper().convertToString(arg)); + headerCodec.encode(clientRequest, paramPath, arg); } @Override @@ -103,7 +86,8 @@ public HeaderProcessorCreator() { } @Override - public ParamValueProcessor create(Parameter parameter, Type genericParamType) { + public ParamValueProcessor create(OperationMeta operationMeta, + String parameterName, Parameter parameter, Type genericParamType) { JavaType targetType = genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); return new HeaderProcessor((HeaderParameter) parameter, targetType); diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessor.java index 8c933a2a1d1..5d5dfc32cf0 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessor.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessor.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.common.rest.codec.param; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessorCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessorCreator.java index b5d87b2f286..1c3378dc6bb 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessorCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessorCreator.java @@ -19,8 +19,11 @@ import java.lang.reflect.Type; -import io.swagger.models.parameters.Parameter; +import org.apache.servicecomb.core.definition.OperationMeta; -public interface ParamValueProcessorCreator { - ParamValueProcessor create(Parameter parameter, Type genericParamType); +public interface ParamValueProcessorCreator { + default ParamValueProcessor create(OperationMeta operationMeta, String paramName, T parameter, + Type genericParamType) { + throw new IllegalStateException("not implemented"); + } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/PathProcessorCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/PathProcessorCreator.java index 71d585eb04b..861b7490fea 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/PathProcessorCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/PathProcessorCreator.java @@ -21,19 +21,18 @@ import java.nio.charset.StandardCharsets; import java.util.Map; -import javax.servlet.http.HttpServletRequest; - import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.springframework.util.StringUtils; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; -import io.swagger.models.parameters.Parameter; -import io.swagger.models.parameters.PathParameter; -import org.springframework.util.StringUtils; +import io.swagger.v3.oas.models.parameters.Parameter; +import jakarta.servlet.http.HttpServletRequest; -public class PathProcessorCreator implements ParamValueProcessorCreator { +public class PathProcessorCreator implements ParamValueProcessorCreator { public static final String PARAMTYPE = "path"; public static class PathProcessor extends AbstractParamProcessor { @@ -72,9 +71,10 @@ public PathProcessorCreator() { } @Override - public ParamValueProcessor create(Parameter parameter, Type genericParamType) { + public ParamValueProcessor create(OperationMeta operationMeta, + String parameterName, Parameter parameter, Type genericParamType) { JavaType targetType = genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); - return new PathProcessor(parameter.getName(), targetType, ((PathParameter) parameter).getDefaultValue(), true); + return new PathProcessor(parameterName, targetType, parameter.getSchema().getDefault(), true); } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/QueryProcessorCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/QueryProcessorCreator.java index ad633201e39..8fef056867c 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/QueryProcessorCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/QueryProcessorCreator.java @@ -18,49 +18,52 @@ package org.apache.servicecomb.common.rest.codec.param; import java.lang.reflect.Type; +import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.Response.Status; - -import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.query.QueryCodec; import org.apache.servicecomb.common.rest.codec.query.QueryCodecsUtils; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; -import com.netflix.config.DynamicPropertyFactory; -import io.swagger.models.parameters.Parameter; -import io.swagger.models.parameters.QueryParameter; -import io.swagger.models.properties.ArrayProperty; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.QueryParameter; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Response.Status; -public class QueryProcessorCreator implements ParamValueProcessorCreator { +@SuppressWarnings("unchecked") +public class QueryProcessorCreator implements ParamValueProcessorCreator { public static final String PARAMTYPE = "query"; public static class QueryProcessor extends AbstractParamProcessor { // This configuration is used for temporary use only. Do not use it if you are sure how it works. And may be deleted in future. - private final boolean emptyAsNull = DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.rest.parameter.query.emptyAsNull", false).get(); + private final boolean emptyAsNull = LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.parameter.query.emptyAsNull", false); // This configuration is used for temporary use only. Do not use it if you are sure how it works. And may be deleted in future. - private final boolean ignoreDefaultValue = DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", false).get(); + private final boolean ignoreDefaultValue = LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", false); // This configuration is used for temporary use only. Do not use it if you are sure how it works. And may be deleted in future. - private final boolean ignoreRequiredCheck = DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", false).get(); + private final boolean ignoreRequiredCheck = LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", false); private final boolean repeatedType; private final QueryCodec queryCodec; public QueryProcessor(QueryParameter queryParameter, JavaType targetType) { - super(queryParameter.getName(), targetType, queryParameter.getDefaultValue(), queryParameter.getRequired()); + super(queryParameter.getName(), targetType, queryParameter.getSchema().getDefault(), + queryParameter.getRequired() != null && queryParameter.getRequired()); - this.repeatedType = ArrayProperty.isType(queryParameter.getType()); - this.queryCodec = QueryCodecsUtils.find(queryParameter.getCollectionFormat()); + this.repeatedType = queryParameter.getSchema() instanceof ArraySchema; + this.queryCodec = QueryCodecsUtils.find(queryParameter.getStyle(), queryParameter.getExplode()); } @Override @@ -70,8 +73,16 @@ public Object getValue(HttpServletRequest request) { public Object getAndCheckParameter(HttpServletRequest request) { Object value = request.getParameter(paramPath); + + // compatible to SpringMVC @RequestParam. BODY_PARAMETER is only set for SpringMVC. + if (value == null) { + Map forms = (Map) request.getAttribute(RestConst.BODY_PARAMETER); + value = (forms == null || forms.get(paramPath) == null) + ? null : forms.get(paramPath); + } + // make some old systems happy - if (emptyAsNull && StringUtils.isEmpty((String) value)) { + if (emptyAsNull && "".equals(value)) { value = null; } @@ -80,7 +91,8 @@ public Object getAndCheckParameter(HttpServletRequest request) { private Object checkRequiredAndDefaultValue() { if (!ignoreRequiredCheck && isRequired()) { - throw new InvocationException(Status.BAD_REQUEST, "Parameter is required."); + throw new InvocationException(Status.BAD_REQUEST, + String.format("Parameter %s is required.", paramPath)); } Object defaultValue = getDefaultValue(); if (!ignoreDefaultValue && defaultValue != null) { @@ -118,7 +130,8 @@ public QueryProcessorCreator() { } @Override - public ParamValueProcessor create(Parameter parameter, Type genericParamType) { + public ParamValueProcessor create(OperationMeta operationMeta, + String parameterName, Parameter parameter, Type genericParamType) { JavaType targetType = genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); return new QueryProcessor((QueryParameter) parameter, targetType); diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java deleted file mode 100644 index 2ca0701b064..00000000000 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java +++ /dev/null @@ -1,327 +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.servicecomb.common.rest.codec.param; - -import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE; -import static javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA; - -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; - -import javax.servlet.http.Part; -import javax.ws.rs.core.MediaType; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.servicecomb.common.rest.codec.RestClientRequest; -import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; -import org.apache.servicecomb.foundation.common.utils.PartUtils; -import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; -import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; -import org.apache.servicecomb.foundation.vertx.stream.InputStreamToReadStream; -import org.apache.servicecomb.foundation.vertx.stream.PumpFromPart; -import org.apache.servicecomb.swagger.invocation.AsyncResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; - -import io.vertx.core.Context; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.MultiMap; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpClientRequest; -import io.vertx.core.http.HttpHeaders; - -public class RestClientRequestImpl implements RestClientRequest { - private static final Logger LOGGER = LoggerFactory.getLogger(RestClientRequestImpl.class); - - protected Context context; - - protected AsyncResponse asyncResp; - - @VisibleForTesting - final Multimap uploads = ArrayListMultimap.create(); - - protected HttpClientRequest request; - - protected Map cookieMap; - - protected Map formMap; - - protected Buffer bodyBuffer; - - private final Handler throwableHandler; - - public RestClientRequestImpl(HttpClientRequest request, Context context, AsyncResponse asyncResp) { - this(request, context, asyncResp, null); - } - - public RestClientRequestImpl(HttpClientRequest request, Context context, AsyncResponse asyncResp, - Handler throwableHandler) { - this.context = context; - this.asyncResp = asyncResp; - this.request = request; - this.throwableHandler = throwableHandler; - } - - @Override - public void write(Buffer bodyBuffer) { - this.bodyBuffer = bodyBuffer; - } - - @Override - public Buffer getBodyBuffer() throws Exception { - genBodyBuffer(); - return bodyBuffer; - } - - @Override - @SuppressWarnings("unchecked") - public void attach(String name, Object partOrList) { - if (null == partOrList) { - LOGGER.debug("null file is ignored, file name = [{}]", name); - return; - } - - if (partOrList.getClass().isArray()) { - for (Object part : (Object[]) partOrList) { - uploads.put(name, PartUtils.getSinglePart(name, part)); - } - } - - if (List.class.isAssignableFrom(partOrList.getClass())) { - for (Object part : (List) partOrList) { - uploads.put(name, PartUtils.getSinglePart(name, part)); - } - return; - } - - uploads.put(name, PartUtils.getSinglePart(name, partOrList)); - } - - @Override - public Future end() { - writeCookies(); - - if (!uploads.isEmpty()) { - return doEndWithUpload(); - } - - return doEndNormal(); - } - - protected Future doEndWithUpload() { - request.setChunked(true); - - String boundary = "boundary" + UUID.randomUUID(); - putHeader(CONTENT_TYPE, MULTIPART_FORM_DATA + "; charset=UTF-8; boundary=" + boundary); - - return genBodyForm(boundary).onSuccess(v -> attachFiles(boundary)).onFailure(e -> asyncResp.consumerFail(e)); - } - - private Future genBodyForm(String boundary) { - if (formMap == null) { - return Future.succeededFuture(); - } - - try { - try (BufferOutputStream output = new BufferOutputStream()) { - for (Entry entry : formMap.entrySet()) { - output.write(bytesOf("\r\n")); - output.write(bytesOf("--" + boundary + "\r\n")); - output.write(bytesOf("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"\r\n\r\n")); - if (entry.getValue() != null) { - String value = RestObjectMapperFactory.getRestObjectMapper().convertToString(entry.getValue()); - output.write(value.getBytes(StandardCharsets.UTF_8)); - } - } - - return writeBuffer(output.getBuffer()); - } - } catch (Exception e) { - return Future.failedFuture(e); - } - } - - private byte[] bytesOf(String string) { - return string.getBytes(StandardCharsets.UTF_8); - } - - protected Future doEndNormal() { - try { - genBodyBuffer(); - } catch (Exception e) { - asyncResp.consumerFail(e); - return Future.succeededFuture(); - } - - if (bodyBuffer == null) { - return request.end(); - } - - return request.end(bodyBuffer); - } - - private void attachFiles(String boundary) { - Iterator> uploadsIterator = uploads.entries().iterator(); - attachFile(boundary, uploadsIterator); - } - - private void attachFile(String boundary, Iterator> uploadsIterator) { - if (!uploadsIterator.hasNext()) { - writeBuffer(boundaryEndInfo(boundary)).onSuccess(v -> request.end()).onFailure(e -> asyncResp.consumerFail(e)); - return; - } - - Entry entry = uploadsIterator.next(); - // do not use part.getName() to get parameter name - // because pojo consumer not easy to set name to part - String name = entry.getKey(); - Part part = entry.getValue(); - String filename = part.getSubmittedFileName(); - - LOGGER.debug("Start attach file [{}:{}].", name, filename); - writeBuffer(fileBoundaryInfo(boundary, name, part)).onSuccess(r -> - new PumpFromPart(context, part).toWriteStream(request, throwableHandler).whenComplete((v, e) -> { - if (e != null) { - LOGGER.warn("Failed attach file [{}:{}].", name, filename, e); - asyncResp.consumerFail(e); - return; - } - - LOGGER.debug("Finish attach file [{}:{}].", name, filename); - attachFile(boundary, uploadsIterator); - })).onFailure(e -> asyncResp.consumerFail(e)); - } - - private Buffer boundaryEndInfo(String boundary) { - return Buffer.buffer() - .appendString("\r\n") - .appendString("--" + boundary + "--\r\n"); - } - - protected Buffer fileBoundaryInfo(String boundary, String name, Part part) { - Buffer buffer = Buffer.buffer(); - buffer.appendString("\r\n"); - buffer.appendString("--" + boundary + "\r\n"); - buffer.appendString("Content-Disposition: form-data; name=\"") - .appendString(name) - .appendString("\"; filename=\"") - .appendString(part.getSubmittedFileName() != null ? part.getSubmittedFileName() : "null") - .appendString("\"\r\n"); - buffer.appendString("Content-Type: ").appendString(part.getContentType()).appendString("\r\n"); - buffer.appendString("Content-Transfer-Encoding: binary\r\n"); - buffer.appendString("\r\n"); - return buffer; - } - - protected Future writeBuffer(Buffer buffer) { - return new InputStreamToReadStream(context, - new BufferInputStream(buffer.getByteBuf()), true).pipe().endOnComplete(false).to(request); - } - - private void genBodyBuffer() throws Exception { - if (bodyBuffer != null) { - return; - } - - if (formMap == null) { - return; - } - - request.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED); - try (BufferOutputStream output = new BufferOutputStream()) { - for (Entry entry : formMap.entrySet()) { - output.write(entry.getKey().getBytes(StandardCharsets.UTF_8)); - output.write('='); - if (entry.getValue() != null) { - String value = RestObjectMapperFactory.getRestObjectMapper().convertToString(entry.getValue()); - value = URLEncoder.encode(value, StandardCharsets.UTF_8.name()); - output.write(value.getBytes(StandardCharsets.UTF_8)); - } - output.write('&'); - } - bodyBuffer = output.getBuffer(); - } - } - - private void writeCookies() { - if (cookieMap == null) { - return; - } - - StringBuilder builder = new StringBuilder(); - for (Entry entry : cookieMap.entrySet()) { - builder.append(entry.getKey()) - .append('=') - .append(entry.getValue()) - .append("; "); - } - request.putHeader(HttpHeaders.COOKIE, builder.toString()); - } - - public Context getContext() { - return context; - } - - public HttpClientRequest getRequest() { - return request; - } - - public Map getCookieMap() { - return cookieMap; - } - - @Override - public void addCookie(String name, String value) { - if (cookieMap == null) { - cookieMap = new HashMap<>(); - } - - cookieMap.put(name, value); - } - - @Override - public void addForm(String name, Object value) { - if (formMap == null) { - formMap = new HashMap<>(); - } - - if (value != null) { - formMap.put(name, value); - } - } - - @Override - public void putHeader(String name, String value) { - request.putHeader(name, value); - } - - @Override - public MultiMap getHeaders() { - return request.headers(); - } -} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceEventStreamProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceEventStreamProcessor.java new file mode 100644 index 00000000000..64795bf817d --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceEventStreamProcessor.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.servicecomb.common.rest.codec.produce; + +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; + +import com.fasterxml.jackson.databind.JavaType; + +import jakarta.ws.rs.core.MediaType; + +public class ProduceEventStreamProcessor implements ProduceProcessor { + private int writeIndex = 0; + + @Override + public String getName() { + return MediaType.SERVER_SENT_EVENTS; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public void doEncodeResponse(OutputStream output, Object result) throws Exception { + String buffer = "id: " + (writeIndex++) + "\n" + + "data: " + + RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(result) + + "\n\n"; + output.write(buffer.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { + String buffer = new String(input.readAllBytes(), StandardCharsets.UTF_8); + for (String line : buffer.split("\n")) { + if (line.startsWith("data: ")) { + return RestObjectMapperFactory.getRestObjectMapper().readValue(line.substring(5), type); + } + } + return null; + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.java index 61989afc7b0..e14451389e5 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.java @@ -20,7 +20,7 @@ import java.io.InputStream; import java.io.OutputStream; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.java index bcf7cb74b2c..574c1e1b630 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.java @@ -72,7 +72,7 @@ default Object decodeResponse(Buffer buffer, JavaType type) throws Exception { return null; } - try (BufferInputStream input = new BufferInputStream(buffer.getByteBuf())) { + try (BufferInputStream input = new BufferInputStream(buffer)) { return doDecodeResponse(input, type); } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java index c5d8693b368..3fdcfec135f 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java @@ -21,13 +21,18 @@ import java.util.List; import java.util.Map; -import javax.ws.rs.core.MediaType; - +import org.apache.http.entity.ContentType; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.RegisterManager; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.util.CollectionUtils; + +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; +import jakarta.ws.rs.core.MediaType; public final class ProduceProcessorManager extends RegisterManager> { private static final Logger LOGGER = LoggerFactory.getLogger(ProduceProcessorManager.class); @@ -37,13 +42,13 @@ public final class ProduceProcessorManager extends RegisterManager nonSerialViewMap = new HashMap<>(); + public static final String PARAM_DEFAULT_RESPONSE_ENCODING = "servicecomb.rest.parameter.default-response-encoding"; + + private static String defaultResponseEncoding; private final Map jsonProcessorMap; @@ -54,7 +59,6 @@ public final class ProduceProcessorManager extends RegisterManager { - nonSerialViewMap.put(processor.getName(), processor); Map prodProcessorMap = getObjMap() .computeIfAbsent(processor.getName(), key -> new HashMap<>()); prodProcessorMap.putIfAbsent(processor.getSerializationView(), processor); @@ -78,30 +82,12 @@ private static ProduceProcessor cloneNewProduceProcessor(Class serialViewClas return produceViewMap.get(DEFAULT_SERIAL_CLASS); } - // key -> accept type - public Map getOrCreateAcceptMap(Class serialViewClass) { - if (serialViewClass == null) { - return nonSerialViewMap; - } - Map result = new HashMap<>(); - getObjMap().forEach((acceptKey, viewMap) -> { - ProduceProcessor produceProcessor = viewMap.computeIfAbsent(serialViewClass.getName(), - viewKey -> cloneNewProduceProcessor(serialViewClass, viewMap)); - result.put(acceptKey, produceProcessor); - }); - return result; - } - - public ProduceProcessor findProcessor(String acceptType, Class serialViewClass) { - Map viewMap = findValue(acceptType); - if (CollectionUtils.isEmpty(viewMap)) { - return null; - } - if (serialViewClass == null) { - return viewMap.get(DEFAULT_SERIAL_CLASS); + private static String defaultResponseEncoding() { + if (defaultResponseEncoding == null) { + defaultResponseEncoding = LegacyPropertyFactory + .getStringProperty(PARAM_DEFAULT_RESPONSE_ENCODING, MediaType.APPLICATION_JSON); } - return viewMap.computeIfAbsent(serialViewClass.getName(), - viewKey -> cloneNewProduceProcessor(serialViewClass, viewMap)); + return defaultResponseEncoding; } public ProduceProcessor findJsonProcessorByViewClass(Class serialViewClass) { @@ -112,14 +98,6 @@ public ProduceProcessor findJsonProcessorByViewClass(Class serialViewClass) { viewKey -> cloneNewProduceProcessor(serialViewClass, jsonProcessorMap)); } - public ProduceProcessor findDefaultProcessorByViewClass(Class serialViewClass) { - if (serialViewClass == null) { - return defaultProcessorMap.get(DEFAULT_SERIAL_CLASS); - } - return defaultProcessorMap.computeIfAbsent(serialViewClass.getName(), - viewKey -> cloneNewProduceProcessor(serialViewClass, defaultProcessorMap)); - } - public ProduceProcessor findPlainProcessorByViewClass(Class serialViewClass) { if (serialViewClass == null) { return plainProcessorMap.get(DEFAULT_SERIAL_CLASS); @@ -139,4 +117,59 @@ public ProduceProcessor findDefaultProcessor() { public ProduceProcessor findDefaultPlainProcessor() { return plainProcessorMap.get(DEFAULT_SERIAL_CLASS); } + + public ProduceProcessor createProduceProcessor(OperationMeta operationMeta, + int statusCode, String accept, Class serialViewClass) { + // If no produces defined, using default processor + ApiResponses responses = operationMeta.getSwaggerOperation().getResponses(); + ApiResponse response = responses.get(String.valueOf(statusCode)); + if (response == null || response.getContent() == null || + response.getContent().size() == 0) { + return findDefaultProcessor(); + } + + // check intersection of `Accept` and `Produces` + if (accept == null) { + if (response.getContent().get(defaultResponseEncoding()) != null) { + accept = defaultResponseEncoding(); + } else { + accept = response.getContent().keySet().iterator().next(); + } + } + + String actualAccept = null; + for (String item : accept.split(",")) { + ContentType contentType = ContentType.parse(item); + if (MediaType.WILDCARD.equals(contentType.getMimeType()) || + MediaType.MEDIA_TYPE_WILDCARD.equals(contentType.getMimeType())) { + if (response.getContent().get(defaultResponseEncoding()) != null) { + actualAccept = defaultResponseEncoding(); + } else { + actualAccept = response.getContent().keySet().iterator().next(); + } + break; + } + if (response.getContent().get(contentType.getMimeType()) != null) { + actualAccept = contentType.getMimeType(); + break; + } + } + + if (actualAccept == null) { + LOGGER.warn("Operation {} do not support accept type {}", operationMeta.getSchemaQualifiedName(), accept); + return findDefaultProcessor(); + } + if (MediaType.APPLICATION_JSON.equals(actualAccept)) { + return findJsonProcessorByViewClass(serialViewClass); + } + if (SwaggerConst.PROTOBUF_TYPE.equals(actualAccept)) { + return new ProduceProtoBufferProcessor(operationMeta, + operationMeta.getSchemaMeta().getSwagger(), response.getContent().get(actualAccept).getSchema()); + } + if (MediaType.SERVER_SENT_EVENTS.equals(actualAccept)) { + return new ProduceEventStreamProcessor(); + } + // text plain + return findPlainProcessorByViewClass(serialViewClass); + } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProtoBufferProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProtoBufferProcessor.java new file mode 100644 index 00000000000..61f56fcacdf --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProtoBufferProcessor.java @@ -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. + */ +package org.apache.servicecomb.common.rest.codec.produce; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.codec.protobuf.utils.ScopedProtobufSchemaManager; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.RootSerializer; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; +import org.apache.servicecomb.swagger.generator.SwaggerConst; + +import com.fasterxml.jackson.databind.JavaType; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; + +public class ProduceProtoBufferProcessor implements ProduceProcessor { + public static final String RESPONSE_MESSAGE_NAME = "X_RESPONSE"; + + public static final String EXT_ID = "protobuf"; + + private static final Object LOCK = new Object(); + + private final OperationMeta operationMeta; + + private final OpenAPI openAPI; + + private final Schema schema; + + private final ScopedProtobufSchemaManager scopedProtobufSchemaManager; + + public ProduceProtoBufferProcessor(OperationMeta operationMeta, OpenAPI openAPI, Schema schema) { + this.operationMeta = operationMeta; + this.openAPI = openAPI; + this.schema = schema; + this.scopedProtobufSchemaManager = getOrCreateScopedProtobufSchemaManager(operationMeta.getMicroserviceMeta()); + } + + private ScopedProtobufSchemaManager getOrCreateScopedProtobufSchemaManager(MicroserviceMeta microserviceMeta) { + ScopedProtobufSchemaManager scopedProtobufSchemaManager = microserviceMeta.getExtData(EXT_ID); + if (scopedProtobufSchemaManager == null) { + synchronized (LOCK) { + scopedProtobufSchemaManager = microserviceMeta.getExtData(EXT_ID); + if (scopedProtobufSchemaManager == null) { + scopedProtobufSchemaManager = new ScopedProtobufSchemaManager(); + microserviceMeta.putExtData(EXT_ID, scopedProtobufSchemaManager); + } + } + } + return scopedProtobufSchemaManager; + } + + @Override + public String getName() { + return SwaggerConst.PROTOBUF_TYPE; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public void doEncodeResponse(OutputStream output, Object result) throws Exception { + ProtoMapper protoMapper = scopedProtobufSchemaManager + .getOrCreateProtoMapper(openAPI, operationMeta.getSchemaId(), + RESPONSE_MESSAGE_NAME, schema); + RootSerializer serializer = protoMapper.getSerializerSchemaManager() + .createRootSerializer(protoMapper.getProto().getMessage(RESPONSE_MESSAGE_NAME), + Object.class); + Map bodyArg = new HashMap<>(1); + bodyArg.put("value", result); + output.write(serializer.serialize(bodyArg)); + } + + @Override + public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { + ProtoMapper protoMapper = scopedProtobufSchemaManager + .getOrCreateProtoMapper(openAPI, operationMeta.getSchemaId(), + RESPONSE_MESSAGE_NAME, schema); + RootDeserializer> deserializer = protoMapper.getDeserializerSchemaManager() + .createRootDeserializer(protoMapper.getProto().getMessage(RESPONSE_MESSAGE_NAME), type); + PropertyWrapper result = deserializer.deserialize(input.readAllBytes()); + return result.getValue(); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java index 0fa64ba9305..21d73688dc4 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java @@ -21,13 +21,13 @@ import java.io.OutputStream; import java.nio.charset.StandardCharsets; -import javax.ws.rs.core.MediaType; - import org.apache.commons.io.IOUtils; import com.fasterxml.jackson.databind.JavaType; -public class ProduceTextPlainProcessor implements ProduceProcessor { +import jakarta.ws.rs.core.MediaType; + +public class ProduceTextPlainProcessor extends ProduceJsonProcessor { @Override public String getName() { return MediaType.TEXT_PLAIN; @@ -35,17 +35,18 @@ public String getName() { @Override public void doEncodeResponse(OutputStream output, Object result) throws Exception { - output.write(String.valueOf(result).getBytes(StandardCharsets.UTF_8)); + if (result instanceof String) { + output.write(((String) result).getBytes(StandardCharsets.UTF_8)); + return; + } + super.doEncodeResponse(output, result); } @Override public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { - // plainText类型,肯定是返回string的,想不出有其他类型的场景 - return IOUtils.toString(input, StandardCharsets.UTF_8); - } - - @Override - public int getOrder() { - return 0; + if (String.class.equals(type.getRawClass())) { + return IOUtils.toString(input, StandardCharsets.UTF_8); + } + return super.doDecodeResponse(input, type); } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/AbstractQueryCodec.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/AbstractQueryCodec.java index 2817f9da830..0ba395bb43f 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/AbstractQueryCodec.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/AbstractQueryCodec.java @@ -20,9 +20,7 @@ import java.util.Collection; import java.util.Collections; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; public abstract class AbstractQueryCodec implements QueryCodec { @@ -39,13 +37,17 @@ public String getCodecName() { @Override @SuppressWarnings("unchecked") - public void encode(URLPathStringBuilder builder, String name, @Nullable Object value) throws Exception { + public void encode(URLPathStringBuilder builder, String name, Object value) throws Exception { if (value == null) { // not write query key to express "null" return; } if (value.getClass().isArray()) { + if (!(value instanceof Object[])) { + value = RestObjectMapperFactory.getRestObjectMapper() + .convertValue(value, Object[].class); + } encode(builder, name, Arrays.asList((Object[]) value)); return; } @@ -58,5 +60,5 @@ public void encode(URLPathStringBuilder builder, String name, @Nullable Object v encode(builder, name, Collections.singletonList(value)); } - abstract void encode(URLPathStringBuilder builder, String name, @Nonnull Collection values) throws Exception; + abstract void encode(URLPathStringBuilder builder, String name, Collection values) throws Exception; } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodec.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodec.java index 61364a806fe..b9bfd1b7130 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodec.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodec.java @@ -20,25 +20,23 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; - import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; import org.springframework.core.Ordered; +import jakarta.servlet.http.HttpServletRequest; + /** * bigger order will override the same name codec */ public interface QueryCodec extends Ordered { - static String encodeValue(@Nonnull Object value) throws UnsupportedEncodingException { + static String encodeValue(Object value) throws UnsupportedEncodingException { return URLEncoder.encode(value.toString(), StandardCharsets.UTF_8.name()); } - // can not replaced by value.toString() because of date serialize - static String convertToString(@Nonnull Object value) throws Exception { + // can not be replaced by value.toString() because of date serialize + static String convertToString(Object value) throws Exception { return RestObjectMapperFactory.getRestObjectMapper().convertToString(value); } @@ -49,7 +47,7 @@ default int getOrder() { String getCodecName(); - void encode(URLPathStringBuilder builder, String name, @Nullable Object value) throws Exception; + void encode(URLPathStringBuilder builder, String name, Object value) throws Exception; Object decode(QueryProcessor processor, HttpServletRequest request); } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecCsv.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecCsv.java index 12e1a6351af..dd745c54b73 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecCsv.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecCsv.java @@ -16,8 +16,11 @@ */ package org.apache.servicecomb.common.rest.codec.query; +/** + * ?query=x1,x2 + */ public class QueryCodecCsv extends QueryCodecWithDelimiter { - public static final String CODEC_NAME = "csv"; + public static final String CODEC_NAME = "form:0"; public static final String DELIMITER = ","; diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecJson.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecJson.java deleted file mode 100644 index d46c28e2ff2..00000000000 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecJson.java +++ /dev/null @@ -1,72 +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.servicecomb.common.rest.codec.query; - -import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.Response.Status; - -import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; -import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; -import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; -import org.apache.servicecomb.core.exception.ExceptionCodes; -import org.apache.servicecomb.core.exception.Exceptions; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -public class QueryCodecJson implements QueryCodec { - public static final String CODEC_NAME = "json"; - - private static final JavaType OBJECT_TYPE = TypeFactory.defaultInstance().constructType(Object.class); - - @Override - public String getCodecName() { - return CODEC_NAME; - } - - @Override - public void encode(URLPathStringBuilder builder, String name, @Nullable Object value) throws Exception { - if (value == null) { - return; - } - - String json = RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(value); - builder.appendQuery(name, QueryCodec.encodeValue(json)); - } - - @Override - public Object decode(QueryProcessor processor, HttpServletRequest request) { - Object value = processor.getAndCheckParameter(request); - if (value == null) { - return null; - } - - try { - JavaType targetType = processor.getTargetType(); - if (targetType == null) { - targetType = OBJECT_TYPE; - } - return RestObjectMapperFactory.getRestObjectMapper().readValue(value.toString(), targetType); - } catch (JsonProcessingException e) { - throw Exceptions - .create(Status.BAD_REQUEST, ExceptionCodes.GENERIC_CLIENT, - "failed to decode query parameter, name=" + processor.getParameterPath()); - } - } -} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMulti.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMulti.java index 49db89b5307..4c2f4287916 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMulti.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMulti.java @@ -17,22 +17,24 @@ package org.apache.servicecomb.common.rest.codec.query; import java.util.Collection; +import java.util.Map; -import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; - +import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; +import jakarta.servlet.http.HttpServletRequest; + +@SuppressWarnings("unchecked") public class QueryCodecMulti extends AbstractQueryCodec { - public static final String CODEC_NAME = "multi"; + public static final String CODEC_NAME = "form:1"; public QueryCodecMulti() { super(CODEC_NAME); } @Override - public void encode(URLPathStringBuilder builder, String name, @Nonnull Collection values) throws Exception { + public void encode(URLPathStringBuilder builder, String name, Collection values) throws Exception { for (Object value : values) { if (value == null) { continue; @@ -48,6 +50,23 @@ public Object decode(QueryProcessor processor, HttpServletRequest request) { if (processor.isRepeatedType()) { //Even if the paramPath does not exist, value won't be null at now String[] values = request.getParameterValues(processor.getParameterPath()); + + // compatible to SpringMVC @RequestParam. BODY_PARAMETER is only set for SpringMVC. + if (values == null || values.length == 0) { + Map forms = (Map) request.getAttribute(RestConst.BODY_PARAMETER); + if (forms == null) { + return processor.convertValue(values); + } + Object formValue = forms.get(processor.getParameterPath()); + if (formValue == null) { + return processor.convertValue(values); + } + if (formValue instanceof String[]) { + values = (String[]) formValue; + } else { + values = new String[] {formValue.toString()}; + } + } return processor.convertValue(values); } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecPipes.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecPipes.java index cf679bca084..68032f004f4 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecPipes.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecPipes.java @@ -16,8 +16,11 @@ */ package org.apache.servicecomb.common.rest.codec.query; +/** + * ?query=x1|x2 + */ public class QueryCodecPipes extends QueryCodecWithDelimiter { - public static final String CODEC_NAME = "pipes"; + public static final String CODEC_NAME = "pipeDelimited:0"; public static final String JOIN_DELIMITER = "|"; diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecSsv.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecSsv.java index c2e56ff726d..514e931d3a0 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecSsv.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecSsv.java @@ -16,8 +16,11 @@ */ package org.apache.servicecomb.common.rest.codec.query; +/** + * ?query=x1%20x2 + */ public class QueryCodecSsv extends QueryCodecWithDelimiter { - public static final String CODEC_NAME = "ssv"; + public static final String CODEC_NAME = "spaceDelimited:0"; public static final String DELIMITER = " "; diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecTsv.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecTsv.java deleted file mode 100644 index 34558d9441d..00000000000 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecTsv.java +++ /dev/null @@ -1,27 +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.servicecomb.common.rest.codec.query; - -public class QueryCodecTsv extends QueryCodecWithDelimiter { - public static final String CODEC_NAME = "tsv"; - - public static final String DELIMITER = "\t"; - - public QueryCodecTsv() { - super(CODEC_NAME, DELIMITER, DELIMITER); - } -} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecWithDelimiter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecWithDelimiter.java index ba9a011666a..f3452bf371e 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecWithDelimiter.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecWithDelimiter.java @@ -19,12 +19,11 @@ import java.util.Collection; import java.util.StringJoiner; -import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; - import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; +import jakarta.servlet.http.HttpServletRequest; + /** * can not support value with delimiter
* a csv example, a collection with two values:
@@ -44,7 +43,7 @@ public QueryCodecWithDelimiter(String codecName, String joinDelimiter, String sp } @Override - public void encode(URLPathStringBuilder builder, String name, @Nonnull Collection values) throws Exception { + public void encode(URLPathStringBuilder builder, String name, Collection values) throws Exception { String joined = join(values); if (joined == null) { return; diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecs.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecs.java index 7ab2c160366..41c5098ef52 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecs.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecs.java @@ -21,9 +21,6 @@ import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +32,6 @@ static QueryCodecs createForTest() { new QueryCodecMulti(), new QueryCodecCsv(), new QueryCodecSsv(), - new QueryCodecTsv(), new QueryCodecPipes() )); } @@ -49,7 +45,7 @@ public QueryCodecs(List orderedCodecs) { defaultCodec = codecs.get(QueryCodecMulti.CODEC_NAME); } - private void register(@Nonnull QueryCodec codec) { + private void register(QueryCodec codec) { QueryCodec exists = codecs.put(codec.getCodecName(), codec); if (exists != null) { LOGGER.info("override QueryCodec, exists={}, new={}.", @@ -57,8 +53,7 @@ private void register(@Nonnull QueryCodec codec) { } } - @Nonnull - public QueryCodec find(@Nullable String name) { + public QueryCodec find(String name) { if (name == null) { return defaultCodec; } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecsUtils.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecsUtils.java index f4aa30d44ea..4b54210c7a0 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecsUtils.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecsUtils.java @@ -16,8 +16,8 @@ */ package org.apache.servicecomb.common.rest.codec.query; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; public class QueryCodecsUtils { // create a default instance, so that more friendly to UT @@ -27,8 +27,15 @@ public QueryCodecsUtils(QueryCodecs queryCodecs) { QueryCodecsUtils.queryCodecs = queryCodecs; } - @Nonnull - public static QueryCodec find(@Nullable String name) { - return queryCodecs.find(name); + public static QueryCodec find(Parameter.StyleEnum styleEnum, Boolean explode) { + return queryCodecs.find(formatName(styleEnum, explode)); + } + + private static String formatName(StyleEnum styleEnum, Boolean explode) { + if (styleEnum == null) { + return null; + } + return styleEnum + ":" + + (explode != null && explode ? "1" : "0"); } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java index 45a2e2d06a6..580175ec680 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java @@ -17,57 +17,43 @@ package org.apache.servicecomb.common.rest.definition; -import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; -import java.util.Locale; import java.util.Map; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; - -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.codec.param.FormProcessorCreator.PartProcessor; -import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; -import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager; import org.apache.servicecomb.common.rest.definition.path.PathRegExp; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.core.definition.OperationMeta; -import org.apache.servicecomb.foundation.common.utils.MimeTypesUtils; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.annotation.JsonView; - -import io.swagger.models.Model; -import io.swagger.models.ModelImpl; -import io.swagger.models.Operation; -import io.swagger.models.Response; -import io.swagger.models.Swagger; -import io.swagger.models.parameters.BodyParameter; -import io.swagger.models.parameters.Parameter; -import io.swagger.models.properties.FileProperty; -import io.swagger.models.properties.Property; -import io.swagger.models.properties.StringProperty; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; +import jakarta.ws.rs.core.MediaType; +@SuppressWarnings("rawtypes") public class RestOperationMeta { private static final Logger LOGGER = LoggerFactory.getLogger(RestOperationMeta.class); protected OperationMeta operationMeta; - protected List produces; - protected boolean formData; // make sure if response is file protected boolean downloadFile; + protected boolean serverSendEvents; + protected List paramList = new ArrayList<>(); // key为参数名 @@ -75,12 +61,6 @@ public class RestOperationMeta { protected List fileKeys = new ArrayList<>(); - // key为数据类型,比如json之类 - private final Map produceProcessorMap = new LinkedHashMap<>(); - - // 不一定等于mgr中的default,因为本operation可能不支持mgr中的default - private ProduceProcessor defaultProcessor; - protected String absolutePath; protected PathRegExp absolutePathRegExp; @@ -91,69 +71,92 @@ public class RestOperationMeta { public void init(OperationMeta operationMeta) { this.operationMeta = operationMeta; - Swagger swagger = operationMeta.getSchemaMeta().getSwagger(); + OpenAPI swagger = operationMeta.getSchemaMeta().getSwagger(); Operation operation = operationMeta.getSwaggerOperation(); - this.produces = operation.getProduces(); - if (produces == null) { - this.produces = swagger.getProduces(); - } this.downloadFile = checkDownloadFileFlag(); - this.createProduceProcessors(); - - // 初始化所有rest param - for (int swaggerParameterIdx = 0; swaggerParameterIdx < operation.getParameters().size(); swaggerParameterIdx++) { - Parameter parameter = operation.getParameters().get(swaggerParameterIdx); + this.serverSendEvents = checkServerSendEvents(); + + if (operation.getParameters() != null) { + for (int swaggerParameterIdx = 0; swaggerParameterIdx < operation.getParameters().size(); swaggerParameterIdx++) { + Parameter parameter = operation.getParameters().get(swaggerParameterIdx); + Type type = operationMeta.getSwaggerProducerOperation() != null ? operationMeta.getSwaggerProducerOperation() + .getSwaggerParameterTypes().get(parameter.getName()) : null; + RestParam param = new RestParam(operationMeta, parameter, type); + addParam(param); + } + } - if ("formData".equals(parameter.getIn())) { + if (operation.getRequestBody() != null) { + if (isFormParameters(operation)) { formData = true; + Schema formSchema = formSchemas(operation); + if (formSchema != null) { + formSchema.getProperties().forEach((k, v) -> { + addRestParamByName(operationMeta, (String) k, operation); + }); + } + } else { + addRestParamByName(operationMeta, + (String) operation.getRequestBody().getExtensions().get(SwaggerConst.EXT_BODY_NAME), operation); } - - Type type = operationMeta.getSwaggerProducerOperation() != null ? operationMeta.getSwaggerProducerOperation() - .getSwaggerParameterTypes().get(parameter.getName()) : null; - type = correctFormBodyType(parameter, type); - RestParam param = new RestParam(parameter, type); - addParam(param); } - setAbsolutePath(concatPath(swagger.getBasePath(), operationMeta.getOperationPath())); + setAbsolutePath(SwaggerUtils.concatAbsolutePath(swagger, operationMeta.getOperationPath())); } - /** - * EdgeService cannot recognize the map type form body whose value type is String, - * so there should be this additional setting. - * @param parameter the swagger information of the parameter - * @param type the resolved param type - * @return the corrected param type - */ - private Type correctFormBodyType(Parameter parameter, Type type) { - if (null != type || !(parameter instanceof BodyParameter)) { - return type; - } - final BodyParameter bodyParameter = (BodyParameter) parameter; - if (!(bodyParameter.getSchema() instanceof ModelImpl)) { - return type; - } - final Property additionalProperties = ((ModelImpl) bodyParameter.getSchema()).getAdditionalProperties(); - if (additionalProperties instanceof StringProperty) { - type = RestObjectMapperFactory.getRestObjectMapper().getTypeFactory() - .constructMapType(Map.class, String.class, String.class); + private void addRestParamByName(OperationMeta operationMeta, String name, Operation operation) { + Type type = operationMeta.getSwaggerProducerOperation() != null ? operationMeta.getSwaggerProducerOperation() + .getSwaggerParameterTypes().get(name) : null; + RestParam param = new RestParam(operationMeta, name, operation.getRequestBody(), formData, type); + addParam(param); + } + + private boolean isFormParameters(Operation operation) { + return operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null || + operation.getRequestBody().getContent().get(SwaggerConst.FILE_MEDIA_TYPE) != null; + } + + private Schema formSchemas(Operation operation) { + if (operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null) { + return operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema(); } - return type; + return operation.getRequestBody().getContent().get(SwaggerConst.FILE_MEDIA_TYPE).getSchema(); } public boolean isDownloadFile() { return downloadFile; } - private boolean checkDownloadFileFlag() { - Response response = operationMeta.getSwaggerOperation().getResponses().get("200"); - if (response != null) { - Model model = response.getResponseSchema(); - return model instanceof ModelImpl && - FileProperty.isType(((ModelImpl) model).getType(), ((ModelImpl) model).getFormat()); + public boolean isServerSendEvents() { + return serverSendEvents; + } + + private boolean checkServerSendEvents() { + ApiResponses responses = operationMeta.getSwaggerOperation().getResponses(); + if (responses == null) { + return false; } + ApiResponse response = responses.get(SwaggerConst.SUCCESS_KEY); + return response != null && response.getContent() != null + && response.getContent().get(MediaType.SERVER_SENT_EVENTS) != null; + } + private boolean checkDownloadFileFlag() { + ApiResponses responses = operationMeta.getSwaggerOperation().getResponses(); + if (responses == null) { + return false; + } + ApiResponse response = responses.get(SwaggerConst.SUCCESS_KEY); + if (response != null && response.getContent() != null) { + for (io.swagger.v3.oas.models.media.MediaType mediaType : response.getContent().values()) { + if (mediaType.getSchema() != null + && "string".equals(mediaType.getSchema().getType()) + && "binary".equals(mediaType.getSchema().getFormat())) { + return true; + } + } + } return false; } @@ -165,21 +168,6 @@ public void setOperationMeta(OperationMeta operationMeta) { this.operationMeta = operationMeta; } - /** - * Concat the two paths to an absolute path, end of '/' added. - * - * e.g. "/" + "/ope" = /ope/ - * e.g. "/prefix" + "/ope" = /prefix/ope/ - */ - private String concatPath(String basePath, String operationPath) { - return ("/" + nonNullify(basePath) + "/" + nonNullify(operationPath) + "/") - .replaceAll("/{2,}", "/"); - } - - private String nonNullify(String path) { - return path == null ? "" : path; - } - public String getAbsolutePath() { return this.absolutePath; } @@ -203,7 +191,7 @@ protected PathRegExp createPathRegExp(String path) { throw new Error("null rest url is not supported"); } try { - return new PathRegExp(path); + return new PathRegExp(OperationLocator.getStandardPath(path)); } catch (Exception e) { LOGGER.error(e.getMessage()); return null; @@ -214,72 +202,10 @@ public RestParam getParamByName(String name) { return paramMap.get(name); } - public RestParam getParamByIndex(int index) { - return paramList.get(index); - } - public OperationMeta getOperationMeta() { return operationMeta; } - protected void createProduceProcessors() { - SwaggerProducerOperation producerOperation = operationMeta.getExtData(Const.PRODUCER_OPERATION); - if (producerOperation != null && producerOperation.getProducerMethod() != null) { - createProduceProcessors(producerOperation.getProducerMethod().getDeclaredAnnotations()); - return; - } - createProduceProcessors(null); - } - - // serialViewClass is deterministic for each operation - protected void createProduceProcessors(Annotation[] annotations) { - if (annotations == null || annotations.length < 1) { - doCreateProduceProcessors(null); - return; - } - for (Annotation annotation : annotations) { - if (annotation.annotationType() == JsonView.class) { - Class[] value = ((JsonView) annotation).value(); - if (value.length != 1) { - throw new IllegalArgumentException( - "@JsonView only supported for exactly 1 class argument "); - } - doCreateProduceProcessors(value[0]); - return; - } - } - doCreateProduceProcessors(null); - } - - // 为operation创建支持的多种produce processor - protected void doCreateProduceProcessors(Class serialViewClass) { - if (null == produces || produces.isEmpty()) { - produceProcessorMap.putAll( - ProduceProcessorManager.INSTANCE.getOrCreateAcceptMap(serialViewClass)); - } else { - for (String produce : produces) { - if (produce.contains(";")) { - produce = produce.substring(0, produce.indexOf(";")); - } - ProduceProcessor processor = ProduceProcessorManager.INSTANCE.findProcessor(produce, serialViewClass); - if (processor == null) { - LOGGER.error("produce {} is not supported, operation={}.", produce, - operationMeta.getMicroserviceQualifiedName()); - continue; - } - this.produceProcessorMap.put(produce, processor); - } - - if (produceProcessorMap.isEmpty()) { - produceProcessorMap.put(ProduceProcessorManager.DEFAULT_TYPE, - ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(serialViewClass)); - } - } - - defaultProcessor = produceProcessorMap.values().stream().findFirst().get(); - produceProcessorMap.putIfAbsent(MediaType.WILDCARD, defaultProcessor); - } - public URLPathBuilder getPathBuilder() { return this.pathBuilder; } @@ -296,37 +222,6 @@ private void addParam(RestParam param) { paramMap.put(param.getParamName(), param); } - public ProduceProcessor findProduceProcessor(String type) { - return this.produceProcessorMap.get(type); - } - - // 选择与accept匹配的produce processor或者缺省的 - public ProduceProcessor ensureFindProduceProcessor(HttpServletRequestEx requestEx) { - String acceptType = requestEx.getHeader(HttpHeaders.ACCEPT); - return ensureFindProduceProcessor(acceptType); - } - - public ProduceProcessor ensureFindProduceProcessor(String acceptType) { - if (downloadFile) { - //do not check accept type, when the produces of provider is text/plain there will return text/plain processor - //when the produces of provider is application/json there will return the application/json processor - //so do not care what accept type the consumer will set. - return this.produceProcessorMap.get(MediaType.WILDCARD); - } - if (StringUtils.isEmpty(acceptType)) { - return defaultProcessor; - } - List mimeTypes = MimeTypesUtils.getSortedAcceptableMimeTypes(acceptType.toLowerCase(Locale.US)); - for (String mime : mimeTypes) { - ProduceProcessor processor = this.produceProcessorMap.get(mime); - if (null != processor) { - return processor; - } - } - - return null; - } - public String getHttpMethod() { return operationMeta.getHttpMethod(); } @@ -334,8 +229,4 @@ public String getHttpMethod() { public List getFileKeys() { return fileKeys; } - - public List getProduces() { - return produces; - } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestParam.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestParam.java index 2615646ea37..a2b44674c6f 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestParam.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestParam.java @@ -21,15 +21,20 @@ import java.util.Collection; import java.util.Map; +import org.apache.servicecomb.common.rest.codec.param.BodyProcessorCreator; +import org.apache.servicecomb.common.rest.codec.param.FormProcessorCreator; import org.apache.servicecomb.common.rest.codec.param.ParamValueProcessor; import org.apache.servicecomb.common.rest.codec.param.ParamValueProcessorCreator; import org.apache.servicecomb.common.rest.codec.param.ParamValueProcessorCreatorManager; +import org.apache.servicecomb.core.definition.OperationMeta; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; -import io.swagger.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; +@SuppressWarnings({"rawtypes", "unchecked"}) public class RestParam { private static final JavaType STRING_ARRAY_TYPE = TypeFactory.defaultInstance().constructArrayType(String.class); @@ -37,12 +42,20 @@ public class RestParam { protected String paramName; - public RestParam(Parameter parameter, Type genericParamType) { + public RestParam(OperationMeta operationMeta, Parameter parameter, Type genericParamType) { this.paramName = parameter.getName(); - init(parameter, genericParamType); + init(operationMeta, parameter, genericParamType); } + public RestParam(OperationMeta operationMeta, + String paramName, RequestBody parameter, boolean isForm, Type genericParamType) { + this.paramName = paramName; + + init(operationMeta, parameter, isForm, genericParamType); + } + + public ParamValueProcessor getParamProcessor() { return this.paramProcessor; } @@ -55,15 +68,28 @@ public String getParamName() { return paramName; } - protected void init(Parameter parameter, Type genericParamType) { + protected void init(OperationMeta operationMeta, Parameter parameter, Type genericParamType) { String paramType = parameter.getIn(); ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.ensureFindValue(paramType); - this.setParamProcessor(creator.create(parameter, genericParamType)); + this.setParamProcessor(creator.create(operationMeta, parameter.getName(), parameter, genericParamType)); + } + + protected void init(OperationMeta operationMeta, RequestBody parameter, boolean isForm, Type genericParamType) { + ParamValueProcessorCreator creator; + if (isForm) { + creator = + ParamValueProcessorCreatorManager.INSTANCE.ensureFindValue(FormProcessorCreator.PARAMTYPE); + } else { + creator = + ParamValueProcessorCreatorManager.INSTANCE.ensureFindValue(BodyProcessorCreator.PARAM_TYPE); + } + + this.setParamProcessor(creator.create(operationMeta, this.paramName, + parameter, genericParamType)); } - @SuppressWarnings("unchecked") public T getValue(Map args) { return (T) args.get(paramName); } @@ -74,7 +100,7 @@ public String[] getValueAsStrings(Map args) { return null; } - if (value.getClass().isArray() || Collection.class.isInstance(value)) { + if (value.getClass().isArray() || value instanceof Collection) { return (String[]) paramProcessor.convertValue(value, STRING_ARRAY_TYPE); } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilder.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilder.java index 8d9e07eadf5..a59c904ff41 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilder.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilder.java @@ -21,8 +21,6 @@ import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; - import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator; import org.apache.servicecomb.common.rest.definition.RestParam; @@ -54,10 +52,6 @@ private void initQueryWriterList(Map paramMap) { } private void initPathWriterList(String rawPath, Map paramMap) { - // 去掉末尾'/' - if (rawPath.endsWith(SLASH)) { - rawPath = rawPath.substring(0, rawPath.length() - 1); - } // 首部加上'/' if (!rawPath.startsWith(SLASH)) { rawPath = SLASH + rawPath; @@ -129,7 +123,7 @@ public URLPathStringBuilder appendPath(String s) { return this; } - public URLPathStringBuilder appendQuery(@Nonnull String name, @Nonnull String encodedValue) { + public URLPathStringBuilder appendQuery(String name, String encodedValue) { if (queryPrefixNotWrite) { stringBuilder.append('?'); queryPrefixNotWrite = false; diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/HttpClientFilter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/HttpClientFilter.java deleted file mode 100644 index 3ae65ff9414..00000000000 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/HttpClientFilter.java +++ /dev/null @@ -1,46 +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.servicecomb.common.rest.filter; - -import java.util.concurrent.CompletableFuture; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; -import org.apache.servicecomb.swagger.invocation.Response; - -public interface HttpClientFilter { - default boolean enabled() { - return true; - } - - int getOrder(); - - /** - * callback method before send a client request. - */ - default CompletableFuture beforeSendRequestAsync(Invocation invocation, HttpServletRequestEx requestEx) { - return CompletableFuture.completedFuture(null); - } - - /** - * @return if finished, then return a none null response
- * if return a null response, then sdk will call next filter.afterReceiveResponse - */ - Response afterReceiveResponse(Invocation invocation, HttpServletResponseEx responseEx); -} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/HttpClientFilterBeforeSendRequestExecutor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/HttpClientFilterBeforeSendRequestExecutor.java deleted file mode 100644 index b536aeb426e..00000000000 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/HttpClientFilterBeforeSendRequestExecutor.java +++ /dev/null @@ -1,92 +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.servicecomb.common.rest.filter; - -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; - -public class HttpClientFilterBeforeSendRequestExecutor { - private final List httpClientFilters; - - private final Invocation invocation; - - private final HttpServletRequestEx requestEx; - - private int currentIndex; - - private final CompletableFuture future = new CompletableFuture<>(); - - public HttpClientFilterBeforeSendRequestExecutor(List httpClientFilters, Invocation invocation, - HttpServletRequestEx requestEx) { - this.httpClientFilters = httpClientFilters; - this.invocation = invocation; - this.requestEx = requestEx; - } - - public CompletableFuture run() { - doRun(); - - return future; - } - - protected CompletableFuture safeInvoke(HttpClientFilter httpClientFilter) { - try { - if (httpClientFilter.enabled()) { - CompletableFuture future = httpClientFilter.beforeSendRequestAsync(invocation, requestEx); - if (future == null) { - future = new CompletableFuture<>(); - future.completeExceptionally(new IllegalStateException( - "HttpClientFilter beforeSendRequestAsync can not return null. Class=" - + httpClientFilter.getClass() - .getName())); - } - return future; - } else { - CompletableFuture eFuture = new CompletableFuture<>(); - eFuture.complete(null); - return eFuture; - } - } catch (Throwable e) { - CompletableFuture eFuture = new CompletableFuture<>(); - eFuture.completeExceptionally(e); - return eFuture; - } - } - - protected void doRun() { - if (currentIndex == httpClientFilters.size()) { - future.complete(null); - return; - } - - HttpClientFilter httpServerFilter = httpClientFilters.get(currentIndex); - currentIndex++; - - CompletableFuture stepFuture = safeInvoke(httpServerFilter); - stepFuture.whenComplete((v, e) -> { - if (e == null) { - doRun(); - return; - } - - future.completeExceptionally(e); - }); - } -} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/HttpServerFilter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/HttpServerFilter.java deleted file mode 100644 index 922e90725d5..00000000000 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/HttpServerFilter.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.servicecomb.common.rest.filter; - -import java.util.concurrent.CompletableFuture; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.definition.OperationMeta; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; -import org.apache.servicecomb.swagger.invocation.Response; - -public interface HttpServerFilter { - int getOrder(); - - default boolean enabled() { - return true; - } - - default boolean needCacheRequest(OperationMeta operationMeta) { - return false; - } - - /** - * @return if finished, then return a none null response
- * if return a null response, then sdk will call next filter.afterReceiveRequest - */ - Response afterReceiveRequest(Invocation invocation, HttpServletRequestEx requestEx); - - /** - * callback method before send a server response. - */ - default CompletableFuture beforeSendResponseAsync(Invocation invocation, HttpServletResponseEx responseEx) { - return CompletableFuture.completedFuture(null); - } -} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/HttpServerFilterBeforeSendResponseExecutor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/HttpServerFilterBeforeSendResponseExecutor.java deleted file mode 100644 index 3c3c23ef2dc..00000000000 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/HttpServerFilterBeforeSendResponseExecutor.java +++ /dev/null @@ -1,92 +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.servicecomb.common.rest.filter; - -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; - -public class HttpServerFilterBeforeSendResponseExecutor { - private final List httpServerFilters; - - private final Invocation invocation; - - private final HttpServletResponseEx responseEx; - - private int currentIndex; - - private final CompletableFuture future = new CompletableFuture<>(); - - public HttpServerFilterBeforeSendResponseExecutor(List httpServerFilters, Invocation invocation, - HttpServletResponseEx responseEx) { - this.httpServerFilters = httpServerFilters; - this.invocation = invocation; - this.responseEx = responseEx; - } - - public CompletableFuture run() { - doRun(); - - return future; - } - - protected CompletableFuture safeInvoke(HttpServerFilter httpServerFilter) { - try { - if (httpServerFilter.enabled()) { - CompletableFuture future = httpServerFilter.beforeSendResponseAsync(invocation, responseEx); - if (future == null) { - future = new CompletableFuture<>(); - future.completeExceptionally(new IllegalStateException( - "HttpServerFilter beforeSendResponseAsync can not return null. Class=" - + httpServerFilter.getClass() - .getName())); - } - return future; - } else { - CompletableFuture eFuture = new CompletableFuture<>(); - eFuture.complete(null); - return eFuture; - } - } catch (Throwable e) { - CompletableFuture eFuture = new CompletableFuture<>(); - eFuture.completeExceptionally(e); - return eFuture; - } - } - - protected void doRun() { - if (currentIndex == httpServerFilters.size()) { - future.complete(null); - return; - } - - HttpServerFilter httpServerFilter = httpServerFilters.get(currentIndex); - currentIndex++; - - CompletableFuture stepFuture = safeInvoke(httpServerFilter); - stepFuture.whenComplete((v, e) -> { - if (e == null) { - doRun(); - return; - } - - future.completeExceptionally(e); - }); - } -} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/ClientRestArgsFilter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/ClientRestArgsFilter.java deleted file mode 100644 index a53283343ed..00000000000 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/ClientRestArgsFilter.java +++ /dev/null @@ -1,62 +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.servicecomb.common.rest.filter.inner; - -import java.util.concurrent.CompletableFuture; - -import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.RestCodec; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; -import org.apache.servicecomb.common.rest.definition.RestOperationMeta; -import org.apache.servicecomb.common.rest.filter.HttpClientFilter; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.definition.OperationMeta; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; -import org.apache.servicecomb.swagger.invocation.Response; - -public class ClientRestArgsFilter implements HttpClientFilter { - - @Override - public int getOrder() { - return -100; - } - - @Override - public CompletableFuture beforeSendRequestAsync(Invocation invocation, HttpServletRequestEx requestEx) { - CompletableFuture result = new CompletableFuture<>(); - RestClientRequestImpl restClientRequest = (RestClientRequestImpl) invocation.getHandlerContext() - .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); - OperationMeta operationMeta = invocation.getOperationMeta(); - RestOperationMeta swaggerRestOperation = operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); - try { - RestCodec.argsToRest(invocation.getSwaggerArguments(), swaggerRestOperation, - restClientRequest); - requestEx.setBodyBuffer(restClientRequest.getBodyBuffer()); - result.complete(null); - } catch (Throwable e) { - result.completeExceptionally(e); - } - return result; - } - - @Override - public Response afterReceiveResponse(Invocation invocation, HttpServletResponseEx responseEx) { - return null; - } -} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java index 4045ba9cc48..1d21e9c8bbd 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java @@ -21,52 +21,62 @@ import static com.google.common.net.HttpHeaders.TRANSFER_ENCODING; import static org.apache.servicecomb.core.exception.Exceptions.toProducerResponse; +import java.io.IOException; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; -import javax.servlet.http.Part; - import org.apache.servicecomb.common.rest.HttpTransportContext; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestCodec; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; +import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.filter.ProducerFilter; -import org.apache.servicecomb.foundation.common.utils.AsyncUtils; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.foundation.common.utils.PartUtils; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.context.TransportContext; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import io.netty.buffer.Unpooled; import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.HttpHeaders; + +public class RestServerCodecFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(RestServerCodecFilter.class); -public class RestServerCodecFilter implements ProducerFilter { public static final String NAME = "rest-server-codec"; - @Nonnull @Override public String getName() { return NAME; } @Override - public int getOrder(InvocationType invocationType, String microservice) { + public int getOrder() { // almost time, should be the first filter. - return Filter.PRODUCER_SCHEDULE_FILTER_ORDER - 2000; + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 2000; } @Override - public boolean isEnabledForTransport(String transport) { - return Const.RESTFUL.equals(transport); + public boolean enabledForTransport(String transport) { + return CoreConst.RESTFUL.equals(transport); } @Override @@ -79,51 +89,149 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne } protected CompletableFuture invokeNext(Invocation invocation, FilterNode nextNode) { + if (invocation.isEdge()) { + TransportContext transportContext = invocation.getTransportContext(); + return nextNode.onFilter(invocation).whenComplete((r, e) -> invocation.setTransportContext(transportContext)); + } return nextNode.onFilter(invocation); } - protected Void decodeRequest(Invocation invocation) { + protected void decodeRequest(Invocation invocation) { + invocation.getInvocationStageTrace().startProviderDecodeRequest(); HttpServletRequestEx requestEx = invocation.getRequestEx(); OperationMeta operationMeta = invocation.getOperationMeta(); RestOperationMeta restOperationMeta = operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); Map swaggerArguments = RestCodec.restToArgs(requestEx, restOperationMeta); invocation.setSwaggerArguments(swaggerArguments); - - return null; + invocation.getInvocationStageTrace().finishProviderDecodeRequest(); } protected CompletableFuture encodeResponse(Invocation invocation, Response response) { invocation.onEncodeResponseStart(response); - HttpTransportContext transportContext = invocation.getTransportContext(); - ProduceProcessor produceProcessor = transportContext.getProduceProcessor(); HttpServletResponseEx responseEx = transportContext.getResponseEx(); - boolean download = isDownloadFileResponseType(invocation, response); + // TODO: response support JsonView + ProduceProcessor produceProcessor = ProduceProcessorManager.INSTANCE + .createProduceProcessor(invocation.getOperationMeta(), response.getStatusCode(), + invocation.getRequestEx().getHeader(HttpHeaders.ACCEPT), null); - return encodeResponse(response, download, produceProcessor, responseEx); + return encodeResponse(invocation, response, produceProcessor, responseEx) + .whenComplete((r, e) -> invocation.onEncodeResponseFinish()); } - @SuppressWarnings("deprecation") - public static CompletableFuture encodeResponse(Response response, boolean download, - ProduceProcessor produceProcessor, HttpServletResponseEx responseEx) { - responseEx.setStatus(response.getStatusCode(), response.getReasonPhrase()); - copyHeadersToHttpResponse(response.getHeaders(), responseEx); + private static boolean isFailedResponse(Response response) { + return response.getResult() instanceof InvocationException; + } - if (download) { - return CompletableFuture.completedFuture(response); + private static CompletableFuture writePart( + HttpServletResponseEx responseEx, Object data, Response response) { + CompletableFuture result = new CompletableFuture<>(); + responseEx.sendPart(PartUtils.getSinglePart(null, data)) + .whenComplete((r, e) -> { + if (e != null) { + result.completeExceptionally(e); + return; + } + result.complete(response); + }); + return result; + } + + private static CompletableFuture writeResponse( + HttpServletResponseEx responseEx, ProduceProcessor produceProcessor, Object data, Response response, + boolean commit) { + try (BufferOutputStream output = new BufferOutputStream(Buffer.buffer())) { + produceProcessor.encodeResponse(output, data); + + CompletableFuture result = new CompletableFuture<>(); + responseEx.setBodyBuffer(output.getBuffer()); // For extensions usage + if (commit) { + responseEx.setContentLength(output.getBuffer().length()); + } + responseEx.sendBuffer(output.getBuffer()).whenComplete((v, e) -> { + if (e != null) { + result.completeExceptionally(e); + return; + } + if (!commit) { + try { + responseEx.flushBuffer(); + } catch (IOException ex) { + LOGGER.warn("Failed to flush buffer for Server Send Events", ex); + } + } + result.complete(response); + }); + return result; + } catch (Throwable e) { + LOGGER.error("internal service error must be fixed.", e); + responseEx.setStatus(500); + return CompletableFuture.failedFuture(e); } + } + + public static CompletableFuture encodeResponse(Invocation invocation, Response response, + ProduceProcessor produceProcessor, HttpServletResponseEx responseEx) { + responseEx.setStatus(response.getStatusCode()); + copyHeadersToHttpResponse(invocation, response.getHeaders(), responseEx); - responseEx.setContentType(produceProcessor.getName() + "; charset=utf-8"); - try (BufferOutputStream output = new BufferOutputStream(Unpooled.compositeBuffer())) { - produceProcessor.encodeResponse(output, response.getResult()); + if (isFailedResponse(response)) { + responseEx.setContentType(produceProcessor.getName()); + return writeResponse(responseEx, produceProcessor, ((InvocationException) response.getResult()).getErrorData(), + response, true); + } - responseEx.setBodyBuffer(output.getBuffer()); + if (isDownloadFileResponseType(invocation, response)) { + return writePart(responseEx, response.getResult(), response); + } - return CompletableFuture.completedFuture(response); - } catch (Throwable e) { - return AsyncUtils.completeExceptionally(e); + if (isServerSendEvent(response)) { + responseEx.setContentType(produceProcessor.getName()); + return writeServerSendEvent(response, produceProcessor, responseEx); } + + responseEx.setContentType(produceProcessor.getName()); + return writeResponse(responseEx, produceProcessor, response.getResult(), response, true); + } + + private static CompletableFuture writeServerSendEvent(Response response, ProduceProcessor produceProcessor, + HttpServletResponseEx responseEx) { + responseEx.setChunked(true); + CompletableFuture result = new CompletableFuture<>(); + Publisher publisher = response.getResult(); + publisher.subscribe(new Subscriber() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + s.request(1); + subscription = s; + } + + @Override + public void onNext(Object o) { + writeResponse(responseEx, produceProcessor, o, response, false).whenComplete((r, e) -> { + if (e != null) { + subscription.cancel(); + result.completeExceptionally(e); + return; + } + subscription.request(1); + }); + } + + @Override + public void onError(Throwable t) { + result.completeExceptionally(t); + } + + @Override + public void onComplete() { + result.complete(response); + } + }); + return result; } /** @@ -137,15 +245,22 @@ public static boolean isDownloadFileResponseType(Invocation invocation, Response invocation.findResponseType(response.getStatusCode()).getRawClass()); } - public static void copyHeadersToHttpResponse(MultiMap headers, HttpServletResponseEx responseEx) { - if (headers == null) { - return; + public static boolean isServerSendEvent(Response response) { + return response.getResult() instanceof Publisher; + } + + public static void copyHeadersToHttpResponse(Invocation invocation, MultiMap headers, + HttpServletResponseEx responseEx) { + if (headers != null) { + headers.remove(CONTENT_LENGTH); + headers.remove(TRANSFER_ENCODING); + for (Entry entry : headers.entries()) { + responseEx.addHeader(entry.getKey(), entry.getValue()); + } } - headers.remove(CONTENT_LENGTH); - headers.remove(TRANSFER_ENCODING); - for (Entry entry : headers.entries()) { - responseEx.addHeader(entry.getKey(), entry.getValue()); + if (invocation != null && responseEx.getHeader(CoreConst.TRACE_ID_NAME) == null) { + responseEx.addHeader(CoreConst.TRACE_ID_NAME, invocation.getTraceId()); } } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/ServerRestArgsFilter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/ServerRestArgsFilter.java deleted file mode 100644 index 63ae8eea92a..00000000000 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/ServerRestArgsFilter.java +++ /dev/null @@ -1,94 +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.servicecomb.common.rest.filter.inner; - -import static org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter.isDownloadFileResponseType; - -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.RestCodec; -import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; -import org.apache.servicecomb.common.rest.definition.RestOperationMeta; -import org.apache.servicecomb.common.rest.filter.HttpServerFilter; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.definition.OperationMeta; -import org.apache.servicecomb.foundation.common.utils.PartUtils; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; -import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; - -import com.netflix.config.DynamicPropertyFactory; - -import io.netty.buffer.Unpooled; - -public class ServerRestArgsFilter implements HttpServerFilter { - private static final boolean enabled = DynamicPropertyFactory.getInstance().getBooleanProperty - ("servicecomb.http.filter.server.serverRestArgs.enabled", true).get(); - - @Override - public int getOrder() { - return -100; - } - - @Override - public boolean enabled() { - return enabled; - } - - @Override - public Response afterReceiveRequest(Invocation invocation, HttpServletRequestEx requestEx) { - OperationMeta operationMeta = invocation.getOperationMeta(); - RestOperationMeta restOperationMeta = operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); - Map swaggerArguments = RestCodec.restToArgs(requestEx, restOperationMeta); - invocation.setSwaggerArguments(swaggerArguments); - return null; - } - - @Override - public CompletableFuture beforeSendResponseAsync(Invocation invocation, HttpServletResponseEx responseEx) { - Response response = (Response) responseEx.getAttribute(RestConst.INVOCATION_HANDLER_RESPONSE); - ProduceProcessor produceProcessor = - (ProduceProcessor) responseEx.getAttribute(RestConst.INVOCATION_HANDLER_PROCESSOR); - Object body = response.getResult(); - if (response.isFailed()) { - body = ((InvocationException) body).getErrorData(); - } - - if (null != invocation && isDownloadFileResponseType(invocation, response)) { - return responseEx.sendPart(PartUtils.getSinglePart(null, body)); - } - - responseEx.setContentType(produceProcessor.getName() + "; charset=utf-8"); - - CompletableFuture future = new CompletableFuture<>(); - try (BufferOutputStream output = new BufferOutputStream(Unpooled.compositeBuffer())) { - produceProcessor.encodeResponse(output, body); - - responseEx.setBodyBuffer(output.getBuffer()); - future.complete(null); - } catch (Throwable e) { - future.completeExceptionally(ExceptionFactory.convertProducerException(e)); - } - return future; - } -} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/WebSocketServerCodecFilter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/WebSocketServerCodecFilter.java new file mode 100644 index 00000000000..79e7368520b --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/WebSocketServerCodecFilter.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.servicecomb.common.rest.filter.inner; + +import static org.apache.servicecomb.core.exception.Exceptions.toProducerResponse; + +import java.util.HashMap; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.common.rest.WebSocketTransportContext; +import org.apache.servicecomb.common.rest.codec.produce.ProduceJsonProcessor; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; +import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.context.TransportContext; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.ServerWebSocket; + +public class WebSocketServerCodecFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServerCodecFilter.class); + + public static final String NAME = "websocket-codec"; + + @Override + public String getName() { + return NAME; + } + + @Override + public int getOrder() { + // almost time, should be the first filter. + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 2000; + } + + @Override + public boolean enabledForTransport(String transport) { + return CoreConst.WEBSOCKET.equals(transport); + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + return CompletableFuture.completedFuture(invocation) + .thenCompose(this::decodeRequest) + .thenCompose(v -> invokeNext(invocation, nextNode)) + .exceptionally(exception -> toProducerResponse(invocation, exception)) + .thenCompose(response -> encodeResponse(invocation, response)); + } + + protected CompletableFuture invokeNext(Invocation invocation, FilterNode nextNode) { + if (invocation.isEdge()) { + TransportContext transportContext = invocation.getTransportContext(); + return nextNode.onFilter(invocation).whenComplete((r, e) -> invocation.setTransportContext(transportContext)); + } + return nextNode.onFilter(invocation); + } + + protected CompletableFuture decodeRequest(Invocation invocation) { + invocation.getInvocationStageTrace().startProviderDecodeRequest(); + invocation.setSwaggerArguments(new HashMap<>()); // set context parameters and do nothing else. + invocation.getInvocationStageTrace().finishProviderDecodeRequest(); + return CompletableFuture.completedFuture(null); + } + + protected CompletableFuture encodeResponse(Invocation invocation, Response response) { + invocation.onEncodeResponseStart(response); + WebSocketTransportContext context = invocation.getTransportContext(); + + return encodeResponse(response, context.getServerWebSocket()) + .whenComplete((r, e) -> invocation.onEncodeResponseFinish()); + } + + private static boolean isFailedResponse(Response response) { + return response.getResult() instanceof InvocationException; + } + + private static CompletableFuture writeResponse( + ServerWebSocket webSocket, Object data, Response response) { + try (BufferOutputStream output = new BufferOutputStream(Buffer.buffer())) { + ProduceJsonProcessor produceProcessor = new ProduceJsonProcessor(); + produceProcessor.encodeResponse(output, data); + CompletableFuture result = new CompletableFuture<>(); + webSocket.write(output.getBuffer()).onComplete(v -> + result.complete(response), result::completeExceptionally); + + return result; + } catch (Throwable e) { + LOGGER.error("internal service error must be fixed.", e); + return CompletableFuture.failedFuture(e); + } + } + + public static CompletableFuture encodeResponse(Response response, ServerWebSocket webSocket) { + if (isFailedResponse(response)) { + return writeResponse(webSocket, ((InvocationException) response.getResult()).getErrorData(), + response); + } + return CompletableFuture.completedFuture(response); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/MicroservicePaths.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/MicroservicePaths.java index 21f014a2c8b..3df5c699403 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/MicroservicePaths.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/MicroservicePaths.java @@ -58,7 +58,7 @@ public void addResource(RestOperationMeta swaggerRestOperation) { protected void addStaticPathResource(RestOperationMeta operation) { String httpMethod = operation.getHttpMethod(); - String path = operation.getAbsolutePath(); + String path = OperationLocator.getStandardPath(operation.getAbsolutePath()); OperationGroup group = staticPathOperations.get(path); if (group == null) { group = new OperationGroup(); @@ -97,10 +97,9 @@ protected void printPath(Collection operations) { for (RestOperationMeta operation : operations) { SwaggerProducerOperation producerOperation = operation.getOperationMeta().getSwaggerProducerOperation(); - LOGGER.info("Swagger mapped \"{[{}], method=[{}], produces={}}\" onto {}", + LOGGER.debug("Swagger mapped \"{[{}], method=[{}]}\" onto {}", operation.getAbsolutePath(), operation.getHttpMethod(), - operation.getProduces(), producerOperation.getProducerMethod()); } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationLocator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationLocator.java index 9a088e70d85..4b94cee9e36 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationLocator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationLocator.java @@ -21,13 +21,13 @@ import java.util.HashMap; import java.util.Map; -import javax.ws.rs.core.Response.Status; - import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jakarta.ws.rs.core.Response.Status; + /** * 从path和http method定位到具体的operation */ @@ -107,9 +107,9 @@ protected boolean checkHttpMethod(RestOperationMeta operation, String httpMethod return operation.getHttpMethod().equals(httpMethod); } - // TODO: almost always change path, this make performance lower. - // Path: /a/b/c -> /a/b/c/ - static String getStandardPath(String path) { + // Make path standard in order to build path mapping and find path operation. + // NOTE: Path: /a/b/c -> /a/b/c/. Almost change path every time, this make performance lower. + public static String getStandardPath(String path) { if (path.length() > 0 && !path.endsWith(SLASH)) { path += SLASH; } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/ServicePathManager.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/ServicePathManager.java index 0876f432f8f..d59d7019bc9 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/ServicePathManager.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/ServicePathManager.java @@ -27,16 +27,12 @@ import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; import org.apache.servicecomb.registry.definition.DefinitionConst; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * 对静态路径和动态路径的operation进行预先处理,加速operation的查询定位 */ public class ServicePathManager { - private static final Logger LOGGER = LoggerFactory.getLogger(ServicePathManager.class); - private static final String REST_PATH_MANAGER = "RestServicePathManager"; protected MicroserviceMeta microserviceMeta; @@ -71,11 +67,6 @@ private void addSchema(SchemaMeta schemaMeta) { operationMeta.putExtData(RestConst.SWAGGER_REST_OPERATION, restOperationMeta); addResource(restOperationMeta); } - - LOGGER.info("add schema to service paths. {}:{}:{}.", - schemaMeta.getAppId(), - schemaMeta.getMicroserviceName(), - schemaMeta.getSchemaId()); } public OperationLocator consumerLocateOperation(String path, String httpMethod) { diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/resource/ClassPathStaticResourceHandler.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/resource/ClassPathStaticResourceHandler.java index 1bc6fa8e429..3884d85a86c 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/resource/ClassPathStaticResourceHandler.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/resource/ClassPathStaticResourceHandler.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.net.URL; -import javax.servlet.http.Part; +import jakarta.servlet.http.Part; import org.apache.servicecomb.foundation.common.part.InputStreamPart; diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/resource/StaticResourceHandler.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/resource/StaticResourceHandler.java index e5655321a06..a01813fbd70 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/resource/StaticResourceHandler.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/resource/StaticResourceHandler.java @@ -19,9 +19,9 @@ import java.io.IOException; import java.net.URI; -import javax.servlet.http.Part; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.Response.Status; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedConfigurationItem.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationItem.java similarity index 97% rename from edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedConfigurationItem.java rename to common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationItem.java index 2fa38461faa..d884e49e453 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedConfigurationItem.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationItem.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.servicecomb.edge.core; +package org.apache.servicecomb.common.rest.route; import java.util.regex.Pattern; diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationLoader.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationLoader.java new file mode 100644 index 00000000000..5c3df2c3355 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationLoader.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.route; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.ConfigUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; + +public class URLMappedConfigurationLoader { + private static final Logger LOG = LoggerFactory.getLogger(URLMappedConfigurationLoader.class); + + private static final String KEY_MAPPING_PATH = ".path"; + + private static final String KEY_MAPPING_SERVICE_NAME = "%s.%s.microserviceName"; + + private static final String KEY_MAPPING_VERSION_RULE = "%s.%s.versionRule"; + + private static final String KEY_MAPPING_PREFIX_SEGMENT_COUNT = "%s.%s.prefixSegmentCount"; + + public static Map loadConfigurations( + Environment environment, String configPrefix) { + Map configurations = new HashMap<>(); + Set configsItems = ConfigUtil.propertiesWithPrefix((ConfigurableEnvironment) environment, configPrefix); + for (String pathKey : configsItems) { + if (pathKey.endsWith(KEY_MAPPING_PATH)) { + URLMappedConfigurationItem configurationItem = new URLMappedConfigurationItem(); + String pattern = environment.getProperty(pathKey); + if (StringUtils.isEmpty(pattern)) { + continue; + } + configurationItem.setPattern(Pattern.compile(pattern)); + configurationItem.setStringPattern(pattern); + String pathKeyItem = pathKey + .substring(configPrefix.length() + 1, pathKey.length() - KEY_MAPPING_PATH.length()); + configurationItem.setMicroserviceName(environment.getProperty( + String.format(KEY_MAPPING_SERVICE_NAME, configPrefix, pathKeyItem))); + if (StringUtils.isEmpty(configurationItem.getMicroserviceName())) { + continue; + } + configurationItem.setPrefixSegmentCount(environment.getProperty( + String.format(KEY_MAPPING_PREFIX_SEGMENT_COUNT, configPrefix, pathKeyItem), int.class, 0)); + configurationItem.setVersionRule(environment.getProperty( + String.format(KEY_MAPPING_VERSION_RULE, configPrefix, pathKeyItem), "0.0.0+")); + configurations.put(pathKeyItem, configurationItem); + } + } + logConfigurations(configurations); + return configurations; + } + + private static void logConfigurations(Map configurations) { + configurations.forEach((key, item) -> LOG.info("config item: key=" + key + ";pattern=" + item.getStringPattern() + + ";service=" + item.getMicroserviceName() + ";versionRule=" + item.getVersionRule())); + } +} diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/Utils.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/Utils.java similarity index 96% rename from edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/Utils.java rename to common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/Utils.java index 9bbefb9f0d1..0e1f64291c9 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/Utils.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/Utils.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.servicecomb.edge.core; +package org.apache.servicecomb.common.rest.route; /** * Commonly used methods in this package. diff --git a/common/common-rest/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module b/common/common-rest/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module index 70251186a5a..65a6c38a587 100644 --- a/common/common-rest/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module +++ b/common/common-rest/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.foundation.common.utils.json.PartModule \ No newline at end of file +org.apache.servicecomb.foundation.common.utils.json.PartModule diff --git a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor index 9353b9dce7c..c13dc4dac85 100644 --- a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor +++ b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor @@ -16,4 +16,4 @@ # org.apache.servicecomb.common.rest.codec.produce.ProduceJsonProcessor -org.apache.servicecomb.common.rest.codec.produce.ProduceTextPlainProcessor \ No newline at end of file +org.apache.servicecomb.common.rest.codec.produce.ProduceTextPlainProcessor diff --git a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpClientFilter b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpClientFilter deleted file mode 100644 index 6fccbe3e684..00000000000 --- a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpClientFilter +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.common.rest.filter.inner.ClientRestArgsFilter \ No newline at end of file diff --git a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter deleted file mode 100644 index 8b2ffebbc50..00000000000 --- a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.common.rest.filter.inner.ServerRestArgsFilter \ No newline at end of file diff --git a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.BootListener b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.BootListener deleted file mode 100644 index 607552cd9af..00000000000 --- a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.BootListener +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.common.rest.RestEngineSchemaListener \ No newline at end of file diff --git a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider index cf4b594be98..94be435a96b 100644 --- a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider +++ b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.common.rest.filter.RestFilterProvider \ No newline at end of file +org.apache.servicecomb.common.rest.filter.RestFilterProvider diff --git a/common/common-rest/src/main/resources/META-INF/spring.factories b/common/common-rest/src/main/resources/META-INF/spring.factories deleted file mode 100644 index ce480f632d6..00000000000 --- a/common/common-rest/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,18 +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. -# -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.common.rest.CommonRestConfiguration diff --git a/common/common-rest/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/common/common-rest/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..8784e8798e8 --- /dev/null +++ b/common/common-rest/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.common.rest.CommonRestConfiguration diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java index 4d7d5443049..50b3e8329d5 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java @@ -17,18 +17,17 @@ package org.apache.servicecomb.common.rest; -import static javax.ws.rs.core.Response.Status.NOT_ACCEPTABLE; -import static javax.ws.rs.core.Response.Status.NOT_FOUND; +import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; +import static org.apache.servicecomb.common.rest.RestConst.DECODE_INVOCATION_CONTEXT; +import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; +import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; -import javax.ws.rs.core.HttpHeaders; - import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.common.rest.locator.ServicePathManager; -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; @@ -38,7 +37,7 @@ import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; @@ -49,6 +48,7 @@ import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; +import org.springframework.core.env.Environment; import io.vertx.core.json.Json; import io.vertx.ext.web.RoutingContext; @@ -81,19 +81,24 @@ public class RestProducerInvocationCreatorTest { static SCBEngine engine; + static Environment environment; + @BeforeAll public static void beforeClass() { - ArchaiusUtils.resetConfig(); - ConfigUtil.installDynamicConfig(); - - engine = SCBBootstrap.createSCBEngineForTest(); + environment = Mockito.mock(Environment.class); + engine = SCBBootstrap.createSCBEngineForTest(environment); engine.setStatus(SCBStatus.UP); + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); + engine.setEnvironment(environment); } @AfterAll public static void afterClass() { engine.destroy(); - ArchaiusUtils.resetConfig(); } @BeforeEach @@ -113,16 +118,15 @@ public void should_failed_when_not_defined_any_schema() { assertThat(throwable.getStatusCode()).isEqualTo(NOT_FOUND.getStatusCode()); assertThat(Json.encode(data)).isIn("{\"code\":\"SCB.00000002\",\"message\":\"Not Found\"}", - "{\"message\":\"Not Found\",\"code\":\"SCB.00000002\"}"); + "{\"message\":\"Not Found\",\"code\":\"SCB.00000002\"}"); } } @Test - public void should_failed_when_accept_is_not_support() { + public void should_save_requestEx_in_invocation_context() { try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { - mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)).thenReturn(servicePathManager); - Mockito.when(requestEx.getHeader(HttpHeaders.ACCEPT)).thenReturn("test-type"); - Mockito.when(restOperationMeta.ensureFindProduceProcessor(requestEx)).thenReturn(null); + mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)) + .thenReturn(servicePathManager); Mockito.when(creator.locateOperation(microserviceMeta)).thenReturn(locator); Mockito.when(locator.getOperation()).thenReturn(restOperationMeta); Mockito.when(restOperationMeta.getOperationMeta()).thenReturn(operationMeta); @@ -130,68 +134,70 @@ public void should_failed_when_accept_is_not_support() { Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); - InvocationException throwable = (InvocationException) catchThrowable(() -> creator.createAsync().join()); - CommonExceptionData data = (CommonExceptionData) throwable.getErrorData(); + Invocation invocation = creator.createAsync().join(); - assertThat(throwable.getStatusCode()).isEqualTo(NOT_ACCEPTABLE.getStatusCode()); - assertThat(Json.encode(data)).isIn("{\"code\":\"SCB.00000000\",\"message\":\"Accept test-type is not supported\"}", - "{\"message\":\"Accept test-type is not supported\",\"code\":\"SCB.00000000\"}"); + Object request = invocation.getLocalContext(RestConst.REST_REQUEST); + assertThat(request).isSameAs(requestEx); } } @Test - public void should_save_requestEx_in_invocation_context() { + public void should_save_path_var_map_in_requestEx() { try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { - mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)).thenReturn(servicePathManager); + mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)) + .thenReturn(servicePathManager); Mockito.when(creator.locateOperation(microserviceMeta)).thenReturn(locator); Mockito.when(locator.getOperation()).thenReturn(restOperationMeta); Mockito.when(restOperationMeta.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); - Mockito.doNothing().when(creator).initProduceProcessor(); - Invocation invocation = creator.createAsync().join(); + creator.createAsync().join(); - Object request = invocation.getLocalContext(RestConst.REST_REQUEST); - assertThat(request).isSameAs(requestEx); + Mockito.verify(requestEx, Mockito.times(1)).setAttribute(Mockito.eq(RestConst.PATH_PARAMETERS), Mockito.any()); } } @Test - public void should_save_path_var_map_in_requestEx() { + public void should_merge_invocation_context_from_request() { + Mockito.when(environment.getProperty(DECODE_INVOCATION_CONTEXT, boolean.class, true)) + .thenReturn(true); try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { - mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)).thenReturn(servicePathManager); + mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)) + .thenReturn(servicePathManager); Mockito.when(creator.locateOperation(microserviceMeta)).thenReturn(locator); Mockito.when(locator.getOperation()).thenReturn(restOperationMeta); Mockito.when(restOperationMeta.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); - Mockito.doNothing().when(creator).initProduceProcessor(); + Mockito.when(requestEx.getHeader(CoreConst.CSE_CONTEXT)).thenReturn("{\"k\":\"v\"}"); - creator.createAsync().join(); + Invocation invocation = creator.createAsync().join(); - Mockito.verify(requestEx, Mockito.times(1)).setAttribute(Mockito.eq(RestConst.PATH_PARAMETERS), Mockito.any()); + assertThat(invocation.getContext("k")).isEqualTo("v"); } } @Test - public void should_merge_invocation_context_from_request() { + public void should_not_merge_invocation_context_from_request() { + Mockito.when(environment.getProperty(DECODE_INVOCATION_CONTEXT, boolean.class, true)) + .thenReturn(false); try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { - mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)).thenReturn(servicePathManager); + mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)) + .thenReturn(servicePathManager); Mockito.when(creator.locateOperation(microserviceMeta)).thenReturn(locator); Mockito.when(locator.getOperation()).thenReturn(restOperationMeta); Mockito.when(restOperationMeta.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); - Mockito.doNothing().when(creator).initProduceProcessor(); - Mockito.when(requestEx.getHeader(Const.CSE_CONTEXT)).thenReturn("{\"k\":\"v\"}"); + Mockito.when(requestEx.getHeader(CoreConst.CSE_CONTEXT)).thenReturn("{\"k\":\"v\"}"); Invocation invocation = creator.createAsync().join(); - assertThat(invocation.getContext("k")).isEqualTo("v"); + assertThat(invocation.getContext("k")).isNull(); } } } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/TestRestEngineSchemaListener.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/TestRestEngineSchemaListener.java index d3cb3ebeb6d..fa619acb725 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/TestRestEngineSchemaListener.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/TestRestEngineSchemaListener.java @@ -17,18 +17,29 @@ package org.apache.servicecomb.common.rest; +import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; +import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; + +import java.util.ArrayList; +import java.util.List; + import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.common.rest.locator.ServicePathManager; import org.apache.servicecomb.common.rest.locator.TestPathSchema; -import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.core.executor.ExecutorManager; +import org.apache.servicecomb.core.transport.TransportManager; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; public class TestRestEngineSchemaListener { static SCBEngine scbEngine; @@ -37,9 +48,29 @@ public class TestRestEngineSchemaListener { @BeforeAll public static void setup() { - ConfigUtil.installDynamicConfig(); - scbEngine = SCBBootstrap.createSCBEngineForTest() - .addProducerMeta("sid1", new TestPathSchema()) + Environment environment = Mockito.mock(Environment.class); + scbEngine = SCBBootstrap.createSCBEngineForTest(environment); + ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); + TransportManager transportManager = Mockito.mock(TransportManager.class); + scbEngine.setTransportManager(transportManager); + scbEngine.setExecutorManager(executorManager); + scbEngine.setEnvironment(environment); + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)) + .thenReturn(BootStrapProperties.DEFAULT_APPLICATION); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)) + .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_NAME); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_ENVIRONMENT)) + .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_ENVIRONMENT); + + List listeners = new ArrayList<>(); + listeners.add(new RestEngineSchemaListener()); + scbEngine.setBootListeners(listeners); + scbEngine.addProducerMeta("sid1", new TestPathSchema()) .run(); spm = ServicePathManager.getServicePathManager(scbEngine.getProducerMicroserviceMeta()); } @@ -47,35 +78,38 @@ public static void setup() { @AfterAll public static void teardown() { scbEngine.destroy(); - ArchaiusUtils.resetConfig(); } @Test public void testLocateNotFound() { InvocationException exception = Assertions.assertThrows(InvocationException.class, - () -> spm.producerLocateOperation("/notExist", "GET")); - Assertions.assertEquals("InvocationException: code=404;msg=CommonExceptionData [message=Not Found]", exception.getMessage()); + () -> spm.producerLocateOperation("/notExist", "GET")); + Assertions.assertEquals("InvocationException: code=404;msg=CommonExceptionData [message=Not Found]", + exception.getMessage()); } @Test public void testLocateNotFoundDynamicRemained() { InvocationException exception = Assertions.assertThrows(InvocationException.class, - () -> spm.producerLocateOperation("/dynamic/1/2", "GET")); - Assertions.assertEquals("InvocationException: code=404;msg=CommonExceptionData [message=Not Found]", exception.getMessage()); + () -> spm.producerLocateOperation("/dynamic/1/2", "GET")); + Assertions.assertEquals("InvocationException: code=404;msg=CommonExceptionData [message=Not Found]", + exception.getMessage()); } @Test public void testLocateStaticMethodNotAllowed() { InvocationException exception = Assertions.assertThrows(InvocationException.class, - () -> spm.producerLocateOperation("/staticEx", "POST")); - Assertions.assertEquals("InvocationException: code=405;msg=CommonExceptionData [message=Method Not Allowed]", exception.getMessage()); + () -> spm.producerLocateOperation("/staticEx", "POST")); + Assertions.assertEquals("InvocationException: code=405;msg=CommonExceptionData [message=Method Not Allowed]", + exception.getMessage()); } @Test public void testLocateDynamicMethodNotAllowed() { InvocationException exception = Assertions.assertThrows(InvocationException.class, - () -> spm.producerLocateOperation("/dynamic/1", "POST")); - Assertions.assertEquals("InvocationException: code=405;msg=CommonExceptionData [message=Method Not Allowed]", exception.getMessage()); + () -> spm.producerLocateOperation("/dynamic/1", "POST")); + Assertions.assertEquals("InvocationException: code=405;msg=CommonExceptionData [message=Method Not Allowed]", + exception.getMessage()); } @Test diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/TestUploadConfig.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/TestUploadConfig.java deleted file mode 100644 index 4081818d057..00000000000 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/TestUploadConfig.java +++ /dev/null @@ -1,76 +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.servicecomb.common.rest; - -import javax.servlet.MultipartConfigElement; - -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class TestUploadConfig { - @BeforeEach - public void setUp() { - ArchaiusUtils.resetConfig(); - } - - @AfterEach - public void tearDown() { - ArchaiusUtils.resetConfig(); - } - - @Test - public void getMultipartConfig_default() { - ArchaiusUtils.setProperty(RestConst.UPLOAD_DIR, "upload"); - - UploadConfig uploadConfig = new UploadConfig(); - MultipartConfigElement multipartConfigElement = uploadConfig.toMultipartConfigElement(); - - Assertions.assertEquals("upload", uploadConfig.getLocation()); - Assertions.assertEquals(-1L, uploadConfig.getMaxFileSize()); - Assertions.assertEquals(-1L, uploadConfig.getMaxSize()); - Assertions.assertEquals(0, uploadConfig.getFileSizeThreshold()); - - Assertions.assertEquals("upload", multipartConfigElement.getLocation()); - Assertions.assertEquals(-1L, multipartConfigElement.getMaxFileSize()); - Assertions.assertEquals(-1L, multipartConfigElement.getMaxRequestSize()); - Assertions.assertEquals(0, multipartConfigElement.getFileSizeThreshold()); - } - - @Test - public void getMultipartConfig_config() { - ArchaiusUtils.setProperty(RestConst.UPLOAD_DIR, "upload"); - ArchaiusUtils.setProperty(RestConst.UPLOAD_MAX_FILE_SIZE, 1); - ArchaiusUtils.setProperty(RestConst.UPLOAD_MAX_SIZE, 2); - ArchaiusUtils.setProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, 3); - - UploadConfig uploadConfig = new UploadConfig(); - MultipartConfigElement multipartConfigElement = uploadConfig.toMultipartConfigElement(); - - Assertions.assertEquals("upload", uploadConfig.getLocation()); - Assertions.assertEquals(1, uploadConfig.getMaxFileSize()); - Assertions.assertEquals(2, uploadConfig.getMaxSize()); - Assertions.assertEquals(3, uploadConfig.getFileSizeThreshold()); - - Assertions.assertEquals("upload", multipartConfigElement.getLocation()); - Assertions.assertEquals(1, multipartConfigElement.getMaxFileSize()); - Assertions.assertEquals(2, multipartConfigElement.getMaxRequestSize()); - Assertions.assertEquals(3, multipartConfigElement.getFileSizeThreshold()); - } -} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.java index 67d2c914765..c134cb46558 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.java @@ -18,18 +18,14 @@ package org.apache.servicecomb.common.rest.codec; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.Response.Status; - import org.apache.servicecomb.common.rest.codec.param.ParamValueProcessor; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.definition.RestParam; import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.jupiter.api.AfterAll; @@ -37,34 +33,44 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.springframework.core.env.Environment; -import io.swagger.models.parameters.HeaderParameter; -import io.swagger.models.parameters.Parameter; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.HeaderParameter; +import io.swagger.v3.oas.models.parameters.Parameter; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Response.Status; public class TestRestCodec { private static RestOperationMeta restOperation; - private static final Map header = new HashMap<>(); - - private static RestClientRequest clientRequest = new RestClientRequestImpl(null, null, null) { - public void putHeader(String name, String value) { - header.put(name, value); - } - }; - private static List paramList = null; + static Environment environment = Mockito.mock(Environment.class); + @BeforeAll public static void beforeClass() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.header.ignoreRequiredCheck", boolean.class, false)) + .thenReturn(false); + Parameter hp = new HeaderParameter(); hp.setName("header"); - RestParam restParam = new RestParam(hp, int.class); + hp.setSchema(new Schema<>()); + RestParam restParam = new RestParam(null, hp, int.class); restOperation = Mockito.mock(RestOperationMeta.class); paramList = new ArrayList<>(); - paramList.add(restParam); Mockito.when(restOperation.getParamList()).thenReturn(paramList); Mockito.when(restOperation.getParamByName("test")).thenReturn(restParam); @@ -73,23 +79,9 @@ public static void beforeClass() { @AfterAll public static void afterClass() { restOperation = null; - clientRequest = null; paramList.clear(); } - @Test - public void testArgsToRest() { - try { - Map args = new HashMap<>(); - args.put("header", "abc"); - RestCodec.argsToRest(args, restOperation, clientRequest); - Assertions.assertEquals("abc", header.get("header")); - } catch (Exception e) { - e.printStackTrace(); - Assertions.fail(); - } - } - @Test public void testRestToArgs() throws Exception { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); @@ -131,7 +123,7 @@ public void testRestToArgsException() throws Exception { success = true; } catch (InvocationException e) { Assertions.assertEquals(400, e.getStatusCode()); - Assertions.assertTrue(((CommonExceptionData) e.getErrorData()).getMessage().contains("Parameter is not valid")); + Assertions.assertTrue(e.getMessage().contains("Parameter is not valid")); } Assertions.assertFalse(success); } @@ -160,6 +152,8 @@ public void testRestToArgsInstanceException() throws Exception { success = true; } catch (InvocationException e) { Assertions.assertEquals(e.getStatusCode(), Status.BAD_REQUEST.getStatusCode()); + Assertions.assertTrue(((CommonExceptionData) e.getErrorData()).getMessage() + .contains("Parameter is not valid for operation")); } Assertions.assertFalse(success); } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestObjectMapper.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestObjectMapper.java index 62c21f71d94..57da9440e4f 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestObjectMapper.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestObjectMapper.java @@ -21,6 +21,9 @@ import java.io.InputStream; import java.util.Date; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.exc.StreamConstraintsException; +import com.google.common.base.Strings; import org.apache.servicecomb.foundation.common.utils.RestObjectMapper; import org.junit.jupiter.api.Assertions; @@ -36,7 +39,7 @@ public class TestRestObjectMapper { @Test - public void testFormateDate() throws Exception { + public void testFormatDate() throws Exception { RestObjectMapper mapper = new RestObjectMapper(); // must read/write ISO 8061 dates Date date = mapper.readValue("\"2017-07-21T17:32:28Z\"".getBytes(), Date.class); @@ -90,4 +93,18 @@ public void testJsonObjectWork() { Assertions.fail(); } } + + @Test + public void testReadValue() { + String content = "{\"desc\":" + Strings.repeat("9", 1001) + "}"; + try { + RestObjectMapperFactory.getRestObjectMapper().readValue(content, PojoModel.class); + Assertions.fail(); + } catch (StreamConstraintsException e) { + // right place, nothing to do. + } catch (JsonProcessingException ex) { + Assertions.fail(); + } + } + } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/fix/TestDoSFix.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/fix/TestDoSFix.java index ae8abc8a6ea..361a3844d4c 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/fix/TestDoSFix.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/fix/TestDoSFix.java @@ -35,7 +35,7 @@ public class TestDoSFix { static final ObjectMapper mapper = new RestObjectMapper(); - static final String invalidNum = Strings.repeat("9", 100_0000); + static final String invalidNum = Strings.repeat("9", 1000); static final String invalidStr = "\"" + invalidNum + "\""; @@ -79,7 +79,7 @@ void fastFail(Callable callable, Class eCls) { long start = System.currentTimeMillis(); try { Object ret = callable.call(); - Assertions.fail("expect failed, but succes to be " + ret); + Assertions.fail("expect failed, but success to be " + ret); } catch (AssertionError e) { throw e; } catch (Throwable e) { diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java index 2db296e4d14..ab71e3a9e99 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java @@ -19,36 +19,42 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; +import java.util.Set; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.param.BodyProcessorCreator.BodyProcessor; import org.apache.servicecomb.common.rest.codec.param.BodyProcessorCreator.RawJsonBodyProcessor; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.definition.SchemaMeta; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.springframework.core.env.Environment; import com.fasterxml.jackson.databind.type.TypeFactory; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.parameters.RequestBody; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import io.vertx.core.buffer.impl.BufferImpl; import io.vertx.core.http.impl.headers.HeadersMultiMap; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; public class TestBodyProcessor { + Environment environment = Mockito.mock(Environment.class); final HttpServletRequest request = Mockito.mock(HttpServletRequest.class); @@ -58,7 +64,7 @@ public class TestBodyProcessor { ParamValueProcessor processor; - final ByteBuf inputBodyByteBuf = Unpooled.buffer(); + final Buffer inputBodyByteBuf = Buffer.buffer(); final BufferInputStream inputStream = new BufferInputStream(inputBodyByteBuf); @@ -67,11 +73,24 @@ public class TestBodyProcessor { String value; private void createProcessor(Class type) { - processor = new BodyProcessor(TypeFactory.defaultInstance().constructType(type), type.equals(String.class), true); + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); + OpenAPI openAPI = Mockito.mock(OpenAPI.class); + Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); + Mockito.when(schemaMeta.getSwagger()).thenReturn(openAPI); + RequestBody requestBody = Mockito.mock(RequestBody.class); + Content content = Mockito.mock(Content.class); + Mockito.when(requestBody.getContent()).thenReturn(content); + Mockito.when(requestBody.getRequired()).thenReturn(true); + Set supported = new HashSet<>(); + supported.add(MediaType.APPLICATION_JSON); + supported.add(MediaType.TEXT_PLAIN); + Mockito.when(content.keySet()).thenReturn(supported); + processor = new BodyProcessor(operationMeta, TypeFactory.defaultInstance().constructType(type), requestBody); } private void createRawJsonProcessor() { - processor = new RawJsonBodyProcessor(TypeFactory.defaultInstance().constructType(String.class), true, true); + processor = new RawJsonBodyProcessor(TypeFactory.defaultInstance().constructType(String.class), true); } private void createClientRequest() { @@ -94,7 +113,10 @@ private void setupGetValue(Class type) throws IOException { @BeforeEach public void before() { - headers = new HeadersMultiMap(); + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) + .thenReturn(false); + headers = HeadersMultiMap.httpHeaders(); value = "value"; } @@ -118,7 +140,7 @@ public void testGetValueNoAttrNoStream() throws Exception { @Test public void testGetValueTextPlain() throws Exception { setupGetValue(String.class); - inputBodyByteBuf.writeCharSequence("abc", StandardCharsets.UTF_8); + inputBodyByteBuf.appendString("abc", StandardCharsets.UTF_8.toString()); Mockito.when(request.getContentType()).thenReturn(MediaType.TEXT_PLAIN); @@ -128,7 +150,7 @@ public void testGetValueTextPlain() throws Exception { @Test public void testGetValueContextTypeJson() throws Exception { setupGetValue(Integer.class); - inputBodyByteBuf.writeCharSequence("\"1\"", StandardCharsets.UTF_8); + inputBodyByteBuf.appendString("\"1\"", StandardCharsets.UTF_8.toString()); Mockito.when(request.getContentType()).thenReturn(MediaType.APPLICATION_JSON); @@ -138,7 +160,7 @@ public void testGetValueContextTypeJson() throws Exception { @Test public void testGetValueDefaultJson() throws Exception { setupGetValue(Integer.class); - inputBodyByteBuf.writeCharSequence("\"1\"", StandardCharsets.UTF_8); + inputBodyByteBuf.appendString("\"1\"", StandardCharsets.UTF_8.toString()); Assertions.assertEquals(1, processor.getValue(request)); } @@ -179,20 +201,6 @@ public void testSetValueTextPlain() throws Exception { Assertions.assertEquals(value, outputBodyBuffer.toString()); } - @Test - public void testSetValueTextPlainTypeMismatch() { - createClientRequest(); - createProcessor(String.class); - headers.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN); - - try { - processor.setValue(clientRequest, new Date()); - Assertions.fail("an exception is expected!"); - } catch (Exception e) { - Assertions.assertEquals(IllegalArgumentException.class, e.getClass()); - Assertions.assertEquals("Content-Type is text/plain while arg type is not String", e.getMessage()); - } - } @Test public void testGetParameterPath() { @@ -210,7 +218,7 @@ public void testGetProcessorType() { public void testGetValueRawJson() throws Exception { createRawJsonProcessor(); initInputStream(); - inputBodyByteBuf.writeCharSequence("\"1\"", StandardCharsets.UTF_8); + inputBodyByteBuf.appendString("\"1\"", StandardCharsets.UTF_8.toString()); Assertions.assertEquals("\"1\"", processor.getValue(request)); } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessorCreator.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessorCreator.java index f9a152a4447..22d7e8d39db 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessorCreator.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessorCreator.java @@ -17,22 +17,45 @@ package org.apache.servicecomb.common.rest.codec.param; +import java.util.HashMap; + import org.apache.servicecomb.common.rest.codec.param.BodyProcessorCreator.BodyProcessor; import org.apache.servicecomb.common.rest.codec.param.BodyProcessorCreator.RawJsonBodyProcessor; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.junit.jupiter.api.Assertions; - -import io.swagger.models.parameters.BodyParameter; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.RequestBody; +@SuppressWarnings({"rawtypes", "unchecked"}) public class TestBodyProcessorCreator { + Environment environment = Mockito.mock(Environment.class); + + @BeforeEach + public void before() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) + .thenReturn(false); + } + @Test public void testCreateNormal() { ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(BodyProcessorCreator.PARAM_TYPE); - BodyParameter param = new BodyParameter(); + RequestBody param = new RequestBody(); + param.setContent(new Content()); + param.getContent().addMediaType(SwaggerConst.DEFAULT_MEDIA_TYPE, new MediaType()); + param.getContent().get(SwaggerConst.DEFAULT_MEDIA_TYPE).setSchema(new Schema()); + param.setExtensions(new HashMap<>()); - ParamValueProcessor processor = creator.create(param, String.class); + ParamValueProcessor processor = creator.create(null, null, param, String.class); Assertions.assertEquals(BodyProcessor.class, processor.getClass()); } @@ -41,10 +64,13 @@ public void testCreateNormal() { public void testCreateRawJson() { ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(BodyProcessorCreator.PARAM_TYPE); - BodyParameter param = new BodyParameter(); - param.setVendorExtension(SwaggerConst.EXT_RAW_JSON_TYPE, true); + RequestBody param = new RequestBody(); + param.setContent(new Content()); + param.getContent().addMediaType(SwaggerConst.DEFAULT_MEDIA_TYPE, new MediaType()); + param.getContent().get(SwaggerConst.DEFAULT_MEDIA_TYPE).setSchema(new Schema()); + param.addExtension(SwaggerConst.EXT_RAW_JSON_TYPE, true); - ParamValueProcessor processor = creator.create(param, String.class); + ParamValueProcessor processor = creator.create(null, null, param, String.class); Assertions.assertEquals(RawJsonBodyProcessor.class, processor.getClass()); } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessor.java index ffe1e36f0df..10cad81b4f4 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessor.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessor.java @@ -21,8 +21,8 @@ import java.util.HashMap; import java.util.Map; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessorCreator.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessorCreator.java index 29a4cbf232d..d3769dcb2c5 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessorCreator.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessorCreator.java @@ -18,20 +18,32 @@ package org.apache.servicecomb.common.rest.codec.param; import org.apache.servicecomb.common.rest.codec.param.CookieProcessorCreator.CookieProcessor; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; - -import io.swagger.models.parameters.CookieParameter; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.CookieParameter; public class TestCookieProcessorCreator { + static Environment environment = Mockito.mock(Environment.class); + + @BeforeAll + public static void beforeClass() { + LegacyPropertyFactory.setEnvironment(environment); + } + @Test public void testCreate() { ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(CookieProcessorCreator.PARAMTYPE); CookieParameter p = new CookieParameter(); p.setName("p1"); - - ParamValueProcessor processor = creator.create(p, String.class); + p.setSchema(new Schema()); + ParamValueProcessor processor = creator.create(null, p.getName(), p, String.class); Assertions.assertEquals(CookieProcessor.class, processor.getClass()); } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestFormProcessor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestFormProcessor.java index 2b7e233e757..42aa5343bef 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestFormProcessor.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestFormProcessor.java @@ -24,22 +24,26 @@ import java.util.Map; import java.util.Set; -import javax.servlet.http.HttpServletRequest; - import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.param.FormProcessorCreator.FormProcessor; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; -import io.swagger.models.parameters.FormParameter; -import io.swagger.models.properties.ArrayProperty; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.MapSchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.RequestBody; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.MediaType; + public class TestFormProcessor { final HttpServletRequest request = Mockito.mock(HttpServletRequest.class); @@ -54,22 +58,35 @@ private FormProcessor createProcessor(String name, Type type) { private FormProcessor createProcessor(String name, Type type, String defaultValue, boolean required) { JavaType javaType = TypeFactory.defaultInstance().constructType(type); - FormParameter formParameter = new FormParameter(); - formParameter.name(name) - .required(required) - .setDefaultValue(defaultValue); + RequestBody formParameter = new RequestBody(); + Content content = new Content(); + MapSchema schema = new MapSchema(); + io.swagger.v3.oas.models.media.MediaType mediaType = new io.swagger.v3.oas.models.media.MediaType(); if (javaType.isContainerType()) { - formParameter.type(ArrayProperty.TYPE); + Schema propertySchema = new ArraySchema(); + schema.addProperty(name, propertySchema); + mediaType.schema(schema); + } else { + Schema propertySchema = new Schema(); + propertySchema.setDefault(defaultValue); + schema.addProperty(name, propertySchema); + mediaType.schema(schema); } - return new FormProcessor(formParameter, javaType); + + content.addMediaType(MediaType.APPLICATION_FORM_URLENCODED, + mediaType); + formParameter.content(content) + .required(required); + + return new FormProcessor(name, formParameter, MediaType.APPLICATION_FORM_URLENCODED, javaType); } @Test public void testGetValueWithAttr() throws Exception { Map forms = new HashMap<>(); forms.put("name", "value"); - Mockito.when(request.getAttribute(RestConst.FORM_PARAMETERS)).thenReturn(forms); + Mockito.when(request.getAttribute(RestConst.BODY_PARAMETER)).thenReturn(forms); ParamValueProcessor processor = createProcessor("name", String.class); Object value = processor.getValue(request); diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestFormProcessorCreator.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestFormProcessorCreator.java index d5a5ac25372..f2b7b9c393e 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestFormProcessorCreator.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestFormProcessorCreator.java @@ -17,21 +17,31 @@ package org.apache.servicecomb.common.rest.codec.param; +import java.util.HashMap; + import org.apache.servicecomb.common.rest.codec.param.FormProcessorCreator.FormProcessor; +import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.junit.jupiter.api.Assertions; - -import io.swagger.models.parameters.FormParameter; import org.junit.jupiter.api.Test; +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.RequestBody; + +@SuppressWarnings({"rawtypes", "unchecked"}) public class TestFormProcessorCreator { @Test public void testCreate() { ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(FormProcessorCreator.PARAMTYPE); - FormParameter p = new FormParameter(); - p.setName("p1"); + RequestBody p = new RequestBody(); + p.setContent(new Content()); + p.getContent().addMediaType(SwaggerConst.FORM_MEDIA_TYPE, new MediaType()); + p.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).setSchema(new Schema()); + p.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().setProperties(new HashMap<>()); - ParamValueProcessor processor = creator.create(p, String.class); + ParamValueProcessor processor = creator.create(null, "p1", p, String.class); Assertions.assertEquals(FormProcessor.class, processor.getClass()); } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java index c3f07c582a1..39bed2e809b 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java @@ -26,23 +26,26 @@ import java.util.Map; import java.util.Set; -import javax.servlet.http.HttpServletRequest; - import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.StdDateFormat; -import io.swagger.models.parameters.HeaderParameter; -import io.swagger.models.properties.ArrayProperty; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.HeaderParameter; +import jakarta.servlet.http.HttpServletRequest; public class TestHeaderProcessor { final HttpServletRequest request = Mockito.mock(HttpServletRequest.class); @@ -59,16 +62,26 @@ private HeaderProcessor createProcessor(String name, Type type, String defaultVa JavaType javaType = TypeFactory.defaultInstance().constructType(type); HeaderParameter headerParameter = new HeaderParameter(); + headerParameter.setSchema(new Schema()); headerParameter.name(name) - .required(required) - .setDefaultValue(defaultValue); + .required(required); + headerParameter.getSchema().setDefault(defaultValue); if (javaType.isContainerType()) { - headerParameter.type(ArrayProperty.TYPE); + headerParameter.schema(new ArraySchema()); } return new HeaderProcessor(headerParameter, javaType); } + Environment environment = Mockito.mock(Environment.class); + + @BeforeEach + void setUp() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.header.ignoreRequiredCheck" + , boolean.class, false)).thenReturn(false); + } + @Test public void testGetValueNormal() throws Exception { Mockito.when(request.getHeader("h1")).thenReturn("h1v"); @@ -180,7 +193,7 @@ public void testSetValueNull() throws Exception { @Test public void testSetValueDateFixed() throws Exception { Date date = new Date(1586957400199L); - String strDate = "2020-04-15T13:30:00.199+00:00"; + String strDate = "2020-04-15T13:30:00.199+00:00"; Mockito.doAnswer(invocation -> { headers.put("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date)); @@ -195,7 +208,7 @@ public void testSetValueDateFixed() throws Exception { @Test public void testSetValueDate() throws Exception { Date date = new Date(); - String strDate = new StdDateFormat().format(date); + String strDate = new StdDateFormat().format(date); Mockito.doAnswer(invocation -> { headers.put("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date)); return null; diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessorCreator.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessorCreator.java index 8a4870a57a8..d275b16b62e 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessorCreator.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessorCreator.java @@ -18,20 +18,35 @@ package org.apache.servicecomb.common.rest.codec.param; import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; - -import io.swagger.models.parameters.HeaderParameter; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.HeaderParameter; public class TestHeaderProcessorCreator { + static Environment environment = Mockito.mock(Environment.class); + + @BeforeAll + public static void beforeClass() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.header.ignoreRequiredCheck", boolean.class, false)) + .thenReturn(false); + } + @Test public void testCreate() { ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(HeaderProcessorCreator.PARAMTYPE); HeaderParameter hp = new HeaderParameter(); hp.setName("h1"); + hp.setSchema(new Schema()); - ParamValueProcessor processor = creator.create(hp, String.class); + ParamValueProcessor processor = creator.create(null, hp.getName(), hp, String.class); Assertions.assertEquals(HeaderProcessor.class, processor.getClass()); } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestPathProcessor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestPathProcessor.java index 39bbb21770a..ff8218768cf 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestPathProcessor.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestPathProcessor.java @@ -20,7 +20,7 @@ import java.util.HashMap; import java.util.Map; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.param.PathProcessorCreator.PathProcessor; diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestPathProcessorCreator.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestPathProcessorCreator.java index 954f62695d9..0f40291c82b 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestPathProcessorCreator.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestPathProcessorCreator.java @@ -19,11 +19,12 @@ import org.apache.servicecomb.common.rest.codec.param.PathProcessorCreator.PathProcessor; import org.junit.jupiter.api.Assertions; - -import io.swagger.models.parameters.Parameter; -import io.swagger.models.parameters.PathParameter; import org.junit.jupiter.api.Test; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.PathParameter; + public class TestPathProcessorCreator { @Test public void testCreate() { @@ -31,8 +32,9 @@ public void testCreate() { ParamValueProcessorCreatorManager.INSTANCE.findValue(PathProcessorCreator.PARAMTYPE); Parameter parameter = new PathParameter(); parameter.setName("path"); + parameter.setSchema(new Schema()); - ParamValueProcessor processor = creator.create(parameter, String.class); + ParamValueProcessor processor = creator.create(null, parameter.getName(), parameter, String.class); Assertions.assertEquals(PathProcessor.class, processor.getClass()); } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessor.java index 8683ef108ff..be18fb8378b 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessor.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessor.java @@ -17,40 +17,61 @@ package org.apache.servicecomb.common.rest.codec.param; -import javax.servlet.http.HttpServletRequest; - import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; -import io.swagger.models.parameters.QueryParameter; -import io.swagger.models.properties.ArrayProperty; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; +import io.swagger.v3.oas.models.parameters.QueryParameter; +import jakarta.servlet.http.HttpServletRequest; public class TestQueryProcessor { + static Environment environment = Mockito.mock(Environment.class); + + @BeforeAll + public static void beforeClass() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) + .thenReturn(false); + } + final HttpServletRequest request = Mockito.mock(HttpServletRequest.class); - private ParamValueProcessor createProcessor(String name, Class type, String collectionFormat) { - return createProcessor(name, type, null, true, collectionFormat); + private ParamValueProcessor createProcessor(String name, Class type, + Parameter.StyleEnum style, boolean explode) { + return createProcessor(name, type, null, true, style, explode); } private ParamValueProcessor createProcessor(String name, Class type, String defaultValue, boolean required, - String collectionFormat) { + Parameter.StyleEnum style, boolean explode) { JavaType javaType = TypeFactory.defaultInstance().constructType(type); QueryParameter queryParameter = new QueryParameter(); queryParameter.name(name) .required(required) - .collectionFormat(collectionFormat) - .setDefaultValue(defaultValue); + .setSchema(new StringSchema()); + queryParameter.getSchema().setDefault(defaultValue); if (javaType.isContainerType()) { - queryParameter.type(ArrayProperty.TYPE); + queryParameter.setSchema(new ArraySchema()); + queryParameter.setExplode(explode); + queryParameter.setStyle(style); } return new QueryProcessor(queryParameter, javaType); } @@ -59,7 +80,7 @@ private ParamValueProcessor createProcessor(String name, Class type, String d public void testGetValueNormal() throws Exception { Mockito.when(request.getParameter("name")).thenReturn("value"); - ParamValueProcessor processor = createProcessor("name", String.class, "multi"); + ParamValueProcessor processor = createProcessor("name", String.class, StyleEnum.FORM, true); Object value = processor.getValue(request); Assertions.assertEquals("value", value); } @@ -68,7 +89,7 @@ public void testGetValueNormal() throws Exception { public void testGetValueContainerType() throws Exception { Mockito.when(request.getParameterValues("name")).thenReturn(new String[] {"value", "value2"}); - ParamValueProcessor processor = createProcessor("name", String[].class, "multi"); + ParamValueProcessor processor = createProcessor("name", String[].class, StyleEnum.FORM, true); String[] value = (String[]) processor.getValue(request); MatcherAssert.assertThat(value, Matchers.arrayContaining("value", "value2")); } @@ -77,14 +98,14 @@ public void testGetValueContainerType() throws Exception { public void testGetValueOnCollectionFormatIsCsv() throws Exception { Mockito.when(request.getParameter("name")).thenReturn("value2,value3"); - ParamValueProcessor processor = createProcessor("name", String[].class, "csv"); + ParamValueProcessor processor = createProcessor("name", String[].class, StyleEnum.FORM, false); String[] value = (String[]) processor.getValue(request); MatcherAssert.assertThat(value, Matchers.arrayContaining("value2", "value3")); } @Test public void testGetProcessorType() { - ParamValueProcessor processor = createProcessor("name", String.class, "multi"); + ParamValueProcessor processor = createProcessor("name", String.class, StyleEnum.FORM, true); Assertions.assertEquals("query", processor.getProcessorType()); } @@ -92,12 +113,12 @@ public void testGetProcessorType() { public void testGetValueRequiredTrue() throws Exception { Mockito.when(request.getParameter("name")).thenReturn(null); - ParamValueProcessor processor = createProcessor("name", String.class, "multi"); + ParamValueProcessor processor = createProcessor("name", String.class, StyleEnum.FORM, true); try { processor.getValue(request); Assertions.assertEquals("required is true, throw exception", "not throw exception"); } catch (Exception e) { - Assertions.assertTrue(e.getMessage().contains("Parameter is required.")); + Assertions.assertTrue(e.getMessage().contains("Parameter name is required.")); } } @@ -105,7 +126,7 @@ public void testGetValueRequiredTrue() throws Exception { public void testGetValueRequiredFalse() throws Exception { Mockito.when(request.getParameter("name")).thenReturn(null); - ParamValueProcessor processor = createProcessor("name", String.class, "test", false, "multi"); + ParamValueProcessor processor = createProcessor("name", String.class, "test", false, StyleEnum.FORM, true); Object result = processor.getValue(request); Assertions.assertEquals("test", result); } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessorCreator.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessorCreator.java index f7387db4c57..a75e1df2b2f 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessorCreator.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessorCreator.java @@ -17,28 +17,43 @@ package org.apache.servicecomb.common.rest.codec.param; -import javax.servlet.http.HttpServletRequest; - import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.springframework.core.env.Environment; import com.fasterxml.jackson.databind.type.TypeFactory; -import io.swagger.models.parameters.Parameter; -import io.swagger.models.parameters.QueryParameter; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.QueryParameter; +import jakarta.servlet.http.HttpServletRequest; public class TestQueryProcessorCreator { + static Environment environment = Mockito.mock(Environment.class); + + @BeforeAll + public static void beforeClass() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) + .thenReturn(false); + } + @Test public void testCreate() { ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(QueryProcessorCreator.PARAMTYPE); Parameter parameter = new QueryParameter(); parameter.setName("query"); - - ParamValueProcessor processor = creator.create(parameter, String.class); + parameter.setSchema(new Schema()); + ParamValueProcessor processor = creator.create(null, parameter.getName(), parameter, String.class); Assertions.assertEquals(QueryProcessor.class, processor.getClass()); @@ -56,13 +71,15 @@ public void testCreate() { @Test public void testCreateNullAsEmpty() throws Exception { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); - ArchaiusUtils.setProperty("servicecomb.rest.parameter.query.emptyAsNull", "true"); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) + .thenReturn(true); ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(QueryProcessorCreator.PARAMTYPE); Parameter parameter = new QueryParameter(); parameter.setName("query"); + parameter.setSchema(new Schema()); - ParamValueProcessor processor = creator.create(parameter, String.class); + ParamValueProcessor processor = creator.create(null, parameter.getName(), parameter, String.class); Assertions.assertEquals(QueryProcessor.class, processor.getClass()); @@ -78,6 +95,5 @@ public void testCreateNullAsEmpty() throws Exception { result = (String) processor.convertValue(null, TypeFactory.defaultInstance().constructType(String.class)); result = (String) processor.getValue(request); Assertions.assertNull(result); - ArchaiusUtils.resetConfig(); } } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java deleted file mode 100644 index 87abba9226f..00000000000 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java +++ /dev/null @@ -1,190 +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.servicecomb.common.rest.codec.param; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import javax.servlet.http.Part; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; - -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -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 org.junit.jupiter.api.condition.EnabledForJreRange; -import org.junit.jupiter.api.condition.EnabledOnJre; -import org.junit.jupiter.api.condition.JRE; -import org.mockito.MockedStatic; -import org.mockito.Mockito; - -import com.google.common.collect.Multimap; - -import io.vertx.core.Context; -import io.vertx.core.MultiMap; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpClientRequest; -import static org.assertj.core.api.Assertions.assertThat; - -public class TestRestClientRequestImpl { - private HttpClientRequest request; - - private final Context context = Mockito.mock(Context.class); - - @BeforeEach - public void before() { - request = Mockito.mock(HttpClientRequest.class); - } - - @AfterEach - public void after() { - Mockito.reset(request); - } - - @Test - public void testForm() throws Exception { - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, null, null); - restClientRequest.addForm("abc", "Hello"); - restClientRequest.addForm("def", "world"); - restClientRequest.addForm("ghi", null); - Buffer buffer = restClientRequest.getBodyBuffer(); - assertThat(buffer.toString()).isIn("def=world&abc=Hello&", "abc=Hello&def=world&"); - } - - @Test - public void testCookie() throws Exception { - final MultiMap map = MultiMap.caseInsensitiveMultiMap(); - Mockito.doAnswer(invocation -> { - map.add(io.vertx.core.http.HttpHeaders.COOKIE, "sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; "); - return null; - }).when(request).putHeader(io.vertx.core.http.HttpHeaders.COOKIE, "sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; "); - Mockito.doAnswer(invocation -> { - map.add(io.vertx.core.http.HttpHeaders.COOKIE, "sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; "); - return null; - }).when(request).putHeader(io.vertx.core.http.HttpHeaders.COOKIE, "region=china-north; sessionid=abcdefghijklmnopqrstuvwxyz; "); - Mockito.when(request.headers()).thenReturn(map); - - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, null, null); - restClientRequest.addCookie("sessionid", "abcdefghijklmnopqrstuvwxyz"); - restClientRequest.addCookie("region", "china-north"); - restClientRequest.write(Buffer.buffer("I love servicecomb")); - restClientRequest.end(); - Buffer buffer = restClientRequest.getBodyBuffer(); - Assertions.assertEquals("I love servicecomb", buffer.toString()); - Assertions.assertEquals("sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; ", - restClientRequest.request.headers().get(HttpHeaders.COOKIE)); - } - - @Test - public void fileBoundaryInfo_nullSubmittedFileName() { - Part part = Mockito.mock(Part.class); - Mockito.when(part.getSubmittedFileName()).thenReturn(null); - Mockito.when(part.getContentType()).thenReturn("abc"); - - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, null, null); - Buffer buffer = restClientRequest.fileBoundaryInfo("boundary", "name", part); - Assertions.assertEquals("\r\n" + - "--boundary\r\n" + - "Content-Disposition: form-data; name=\"name\"; filename=\"null\"\r\n" + - "Content-Type: abc\r\n" + - "Content-Transfer-Encoding: binary\r\n" + - "\r\n", buffer.toString()); - } - - @Test - public void fileBoundaryInfo_validSubmittedFileName() { - Part part = Mockito.mock(Part.class); - Mockito.when(part.getSubmittedFileName()).thenReturn("a.txt"); - Mockito.when(part.getContentType()).thenReturn(MediaType.TEXT_PLAIN); - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, null, null); - Buffer buffer = restClientRequest.fileBoundaryInfo("boundary", "name", part); - Assertions.assertEquals("\r\n" + - "--boundary\r\n" + - "Content-Disposition: form-data; name=\"name\"; filename=\"a.txt\"\r\n" + - "Content-Type: text/plain\r\n" + - "Content-Transfer-Encoding: binary\r\n" + - "\r\n", buffer.toString()); - } - - @Test - public void testAttach() { - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, null, null); - Part part = Mockito.mock(Part.class); - String fileName = "fileName"; - - restClientRequest.attach(fileName, part); - - Multimap uploads = restClientRequest.uploads; - Assertions.assertEquals(1, uploads.size()); - MatcherAssert.assertThat(uploads.asMap(), Matchers.hasEntry(fileName, Arrays.asList(part))); - } - - @Test - public void testAttachOnPartIsNull() { - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, null, null); - - restClientRequest.attach("fileName", null); - - Multimap uploads = restClientRequest.uploads; - Assertions.assertTrue(uploads.isEmpty()); - } - - @Test - @EnabledOnJre(JRE.JAVA_8) - public void doEndWithUploadForJre8() { - Map headers = new HashMap<>(); - Mockito.doAnswer(invocation -> { - headers.put(HttpHeaders.CONTENT_TYPE, "multipart/form-data; charset=UTF-8; boundary=boundarynull-null-null-null-null"); - return null; - }).when(request).putHeader(HttpHeaders.CONTENT_TYPE, "multipart/form-data; charset=UTF-8; boundary=boundarynull-null-null-null-null"); - - UUID uuid = new UUID(0, 0); - try (MockedStatic mockedStatic = Mockito.mockStatic(UUID.class)) { - mockedStatic.when(UUID::randomUUID).thenReturn(uuid); - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, context, null); - restClientRequest.doEndWithUpload(); - - Assertions.assertEquals("multipart/form-data; charset=UTF-8; boundary=boundarynull-null-null-null-null", - headers.get(HttpHeaders.CONTENT_TYPE)); - } - } - - @Test - @EnabledForJreRange(min = JRE.JAVA_9) - public void doEndWithUploadAfterJre8() { - Map headers = new HashMap<>(); - Mockito.doAnswer(invocation -> { - headers.put(HttpHeaders.CONTENT_TYPE, "multipart/form-data; charset=UTF-8; boundary=boundary00000000-0000-0000-0000-000000000000"); - return null; - }).when(request).putHeader(HttpHeaders.CONTENT_TYPE, "multipart/form-data; charset=UTF-8; boundary=boundary00000000-0000-0000-0000-000000000000"); - - UUID uuid = new UUID(0, 0); - try (MockedStatic mockedStatic = Mockito.mockStatic(UUID.class)) { - mockedStatic.when(UUID::randomUUID).thenReturn(uuid); - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, context, null); - restClientRequest.doEndWithUpload(); - - Assertions.assertEquals("multipart/form-data; charset=UTF-8; boundary=boundary00000000-0000-0000-0000-000000000000", - headers.get(HttpHeaders.CONTENT_TYPE)); - } - } -} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceTextPlainProcessor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceTextPlainProcessor.java index d7413d93878..d717072a27c 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceTextPlainProcessor.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceTextPlainProcessor.java @@ -52,7 +52,7 @@ public void testdecodeResponseNull() throws Exception { Object result = pp.decodeResponse(Buffer.buffer(), resultType); Assertions.assertNull(result); - ByteArrayInputStream is = new ByteArrayInputStream(new byte[] {}); + ByteArrayInputStream is = new ByteArrayInputStream("\"\"".getBytes(StandardCharsets.UTF_8)); result = pp.decodeResponse(is, resultType); Assertions.assertEquals(result, ""); } @@ -91,6 +91,6 @@ public void testSetSerializationView() { Assertions.assertEquals(DEFAULT_SERIAL_CLASS, pp.getSerializationView()); pp.setSerializationView(Object.class); - Assertions.assertEquals(DEFAULT_SERIAL_CLASS, pp.getSerializationView()); + Assertions.assertEquals("java.lang.Object", pp.getSerializationView()); } } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecCsvTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecCsvTest.java index 3d34e109799..890075cbe7a 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecCsvTest.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecCsvTest.java @@ -19,11 +19,28 @@ import java.util.Date; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; class QueryCodecCsvTest extends QueryCodecTestBase { + static Environment environment = Mockito.mock(Environment.class); + + @BeforeAll + public static void beforeClass() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) + .thenReturn(false); + } + @BeforeEach void setUp() { codec = new QueryCodecCsv(); diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMultiTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMultiTest.java index 759b74d69ad..3c56d1b1ce6 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMultiTest.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMultiTest.java @@ -19,13 +19,27 @@ import java.util.Date; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; class QueryCodecMultiTest extends QueryCodecTestBase { + Environment environment = Mockito.mock(Environment.class); + @BeforeEach void setUp() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) + .thenReturn(false); codec = new QueryCodecMulti(); } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecPipesTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecPipesTest.java index 3b7f33459c2..83f2b80062c 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecPipesTest.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecPipesTest.java @@ -19,11 +19,28 @@ import java.util.Date; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; class QueryCodecPipesTest extends QueryCodecTestBase { + static Environment environment = Mockito.mock(Environment.class); + + @BeforeAll + public static void beforeClass() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) + .thenReturn(false); + } + @BeforeEach void setUp() { codec = new QueryCodecPipes(); diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecSsvTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecSsvTest.java index c719eae0819..c6e521f6718 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecSsvTest.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecSsvTest.java @@ -19,11 +19,28 @@ import java.util.Date; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +public class QueryCodecSsvTest extends QueryCodecTestBase { + static Environment environment = Mockito.mock(Environment.class); + + @BeforeAll + public static void beforeClass() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) + .thenReturn(false); + } -class QueryCodecSsvTest extends QueryCodecTestBase { @BeforeEach void setUp() { codec = new QueryCodecSsv(); diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecTestBase.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecTestBase.java index 5865a2b4c8e..03347607b36 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecTestBase.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecTestBase.java @@ -18,7 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; @@ -26,8 +26,9 @@ import com.fasterxml.jackson.databind.type.TypeFactory; -import io.swagger.models.parameters.QueryParameter; -import io.swagger.models.properties.ArrayProperty; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.QueryParameter; public class QueryCodecTestBase { QueryCodec codec; @@ -67,9 +68,10 @@ private void should_decode(HttpServletRequest request, Object decodedValue) { Class targetType = decodedValue == null ? Object.class : decodedValue.getClass(); QueryParameter queryParameter = new QueryParameter(); - queryParameter.setCollectionFormat(codec.getCodecName()); + queryParameter.setSchema(new Schema()); + queryParameter.getSchema().setFormat(codec.getCodecName()); if (targetType.isArray()) { - queryParameter.setType(ArrayProperty.TYPE); + queryParameter.setSchema(new ArraySchema()); } QueryProcessor queryProcessor = new QueryProcessor(queryParameter, diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecTsvTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecTsvTest.java deleted file mode 100644 index 2e913bf2900..00000000000 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecTsvTest.java +++ /dev/null @@ -1,117 +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.servicecomb.common.rest.codec.query; - -import java.util.Date; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -class QueryCodecTsvTest extends QueryCodecTestBase { - @BeforeEach - void setUp() { - codec = new QueryCodecTsv(); - } - - @Nested - class Encode { - @Test - void should_encode_date() throws Exception { - should_encode("?q=1970-01-01T00%3A00%3A00.000%2B00%3A00", new Date(0)); - } - - @Test - void should_encode_single_value() throws Exception { - should_encode("?q=v1", "v1"); - } - - @Test - void should_encode_empty_string() throws Exception { - should_encode("?q=", ""); - } - - @Test - void should_encode_common_string() throws Exception { - should_encode("?q=v1%09v2", "v1", "v2"); - } - - @Test - void should_encode_common_numbers() throws Exception { - should_encode("?q=1%092", 1, 2); - } - - @Test - void should_encode_chinese_values() throws Exception { - should_encode("?q=%E4%B8%AD%E6%96%87%09v2", "中文", "v2"); - } - - @Test - void should_encode_ignore_null() throws Exception { - should_encode("?q=v1%09v2", "v1", null, "v2"); - } - - @Test - void should_encode_when_values_is_empty_after_ignore_null() throws Exception { - should_encode("", new Object[] {null}); - } - } - - @Nested - class Decode { - @Test - void should_decode_single_value_to_array() { - should_decode("1", new int[] {1}); - } - - @Test - void should_decode_common_values_to_array() { - should_decode("1\t2", new int[] {1, 2}); - } - - @Test - void should_decode_null_to_array() { - should_decode((String) null, new int[] {}); - } - - @Test - void should_decode_empty_string_to_number() { - should_decode("", new int[] {0}); - } - - @Test - void should_decode_empty_string_to_string() { - should_decode("", new String[] {""}); - } - - @Test - void should_decode_common_values_with_empty_string_to_array() { - should_decode("1\t\t2", new int[] {1, 0, 2}); - } - - @Test - void should_decode_values_end_with_delimiter() { - should_decode("1\t\t", new int[] {1, 0, 0}); - } - - @Test - void should_decode_values_start_with_delimiter() { - should_decode("\t\t1", new int[] {0, 0, 1}); - } - } -} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestPath.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestPath.java index 8c1abca8791..db0b09f83fe 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestPath.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestPath.java @@ -25,17 +25,33 @@ import org.apache.servicecomb.common.rest.definition.path.QueryVarParamWriter; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; - -import io.swagger.models.parameters.Parameter; -import io.swagger.models.parameters.PathParameter; -import io.swagger.models.parameters.QueryParameter; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.PathParameter; +import io.swagger.v3.oas.models.parameters.QueryParameter; public class TestPath { + static Environment environment = Mockito.mock(Environment.class); + + @BeforeAll + public static void beforeClass() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) + .thenReturn(false); + } @BeforeEach public void setUp() { @@ -99,12 +115,14 @@ public void testUrlPathBuilder() throws Exception { Parameter pathParameter = new PathParameter(); pathParameter.setName("id"); - RestParam oRestParam = new RestParam(pathParameter, int.class); + pathParameter.setSchema(new Schema<>()); + RestParam oRestParam = new RestParam(null, pathParameter, int.class); paramMap.put(oRestParam.getParamName(), oRestParam); Parameter queryParameter = new QueryParameter(); queryParameter.setName("q"); - oRestParam = new RestParam(queryParameter, String.class); + queryParameter.setSchema(new Schema<>()); + oRestParam = new RestParam(null, queryParameter, String.class); paramMap.put(oRestParam.getParamName(), oRestParam); URLPathBuilder oURLPathBuilder = new URLPathBuilder("/root/{id}", paramMap); @@ -120,7 +138,8 @@ public void testQueryVarParamWriter() { boolean status = true; Parameter parameter = new QueryParameter(); - RestParam restParam = new RestParam(parameter, String.class); + parameter.setSchema(new Schema<>()); + RestParam restParam = new RestParam(null, parameter, String.class); RestParam spy = Mockito.spy(restParam); Mockito.when(spy.getParamName()).thenReturn("queryVar"); QueryVarParamWriter writer = new QueryVarParamWriter(spy); diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationMeta.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationMeta.java index 30e62790959..df8ebe47a75 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationMeta.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationMeta.java @@ -17,50 +17,55 @@ package org.apache.servicecomb.common.rest.definition; +import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; +import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import static org.hamcrest.core.Is.is; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; - -import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; -import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager; -import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.common.rest.RestEngineSchemaListener; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.core.executor.ExecutorManager; +import org.apache.servicecomb.core.transport.TransportManager; import org.hamcrest.MatcherAssert; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; import com.fasterxml.jackson.annotation.JsonView; -import io.swagger.models.Swagger; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.servers.Server; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; public class TestRestOperationMeta { @Path("/") static class RestOperationMetaSchema { @Path("/emptyProduces") @GET - @Produces("") + @Produces(value = MediaType.APPLICATION_JSON) public String emptyProduces() { return null; } @Path("/emptyProducesWithView") @GET - @Produces("") + @Produces(value = MediaType.APPLICATION_JSON) @JsonView(Object.class) public String emptyProducesWithView() { return null; @@ -68,14 +73,14 @@ public String emptyProducesWithView() { @Path("/notSupport") @GET - @Produces("notSupport") + @Produces(value = MediaType.APPLICATION_JSON) public void notSupport() { } @Path("/notSupportWithView") @GET - @Produces("notSupport") + @Produces(value = MediaType.APPLICATION_JSON) @JsonView(Object.class) public void notSupportWithView() { @@ -113,14 +118,14 @@ public String jsonWithView() { @Path("/textCharJsonChar") @GET - @Produces({MediaType.APPLICATION_JSON + ";charset=UTF-8", MediaType.TEXT_PLAIN + ";charset=UTF-8"}) + @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) public void textCharJsonChar() { } @Path("/textCharJsonCharWithView") @GET - @Produces({MediaType.APPLICATION_JSON + ";charset=UTF-8", MediaType.TEXT_PLAIN + ";charset=UTF-8"}) + @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) @JsonView(Object.class) public void textCharJsonCharWithView() { @@ -152,27 +157,46 @@ public void formWithView(@FormParam("form") String form) { } } - static SCBEngine scbEngine; + SCBEngine scbEngine; - static Swagger swagger; + OpenAPI swagger; OperationMeta meta; RestOperationMeta operationMeta; - @BeforeAll - public static void classSetup() { - ConfigUtil.installDynamicConfig(); - scbEngine = SCBBootstrap.createSCBEngineForTest() + @BeforeEach + public void setUp() { + Environment environment = Mockito.mock(Environment.class); + scbEngine = SCBBootstrap.createSCBEngineForTest(environment); + ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); + TransportManager transportManager = Mockito.mock(TransportManager.class); + scbEngine.setTransportManager(transportManager); + scbEngine.setExecutorManager(executorManager); + + Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)) + .thenReturn(BootStrapProperties.DEFAULT_APPLICATION); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)) + .thenReturn("test"); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_ENVIRONMENT)) + .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_ENVIRONMENT); + + List listeners = new ArrayList<>(); + listeners.add(new RestEngineSchemaListener()); + scbEngine.setBootListeners(listeners); + scbEngine .addProducerMeta("sid1", new RestOperationMetaSchema()) .run(); swagger = Mockito.spy(scbEngine.getProducerMicroserviceMeta().ensureFindSchemaMeta("sid1").getSwagger()); } - @AfterAll - public static void classTeardown() { + @AfterEach + public void teardown() { scbEngine.destroy(); - ArchaiusUtils.resetConfig(); } private void findOperation(String operationId) { @@ -183,219 +207,31 @@ private void findOperation(String operationId) { Mockito.when(schemaMeta.getSwagger()).thenReturn(swagger); } - @Test - public void testCreateProduceProcessorsNull() { - findOperation("emptyProduces"); - operationMeta.produces = null; - operationMeta.createProduceProcessors(); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - for (String produce : ProduceProcessorManager.INSTANCE.keys()) { - ProduceProcessor expected = ProduceProcessorManager.INSTANCE.findProcessor(produce, null); - Assertions.assertSame(expected, operationMeta.findProduceProcessor(produce)); - } - } - - @Test - public void testCreateProduceProcessorsNullWithView() { - findOperation("emptyProducesWithView"); - operationMeta.produces = null; - operationMeta.createProduceProcessors(); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - for (String produce : ProduceProcessorManager.INSTANCE.keys()) { - ProduceProcessor expected = ProduceProcessorManager.INSTANCE.findProcessor(produce, Object.class); - Assertions.assertSame(expected, operationMeta.findProduceProcessor(produce)); - } - } - - @Test - public void testCreateProduceProcessorsEmpty() { - findOperation("emptyProduces"); - operationMeta.produces = Arrays.asList(); - operationMeta.createProduceProcessors(); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - for (String produce : ProduceProcessorManager.INSTANCE.keys()) { - ProduceProcessor expected = ProduceProcessorManager.INSTANCE.findProcessor(produce, null); - Assertions.assertSame(expected, operationMeta.findProduceProcessor(produce)); - } - } - - @Test - public void testCreateProduceProcessorsEmptyWithView() { - findOperation("emptyProducesWithView"); - operationMeta.produces = Arrays.asList(); - operationMeta.createProduceProcessors(); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - for (String produce : ProduceProcessorManager.INSTANCE.keys()) { - ProduceProcessor expected = ProduceProcessorManager.INSTANCE.findProcessor(produce, Object.class); - Assertions.assertSame(expected, operationMeta.findProduceProcessor(produce)); - } - } - - @Test - public void testCreateProduceProcessorsNormal() { - findOperation("json"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultJsonProcessor(), - operationMeta.findProduceProcessor(MediaType.APPLICATION_JSON)); - Assertions.assertNull(operationMeta.findProduceProcessor(MediaType.TEXT_PLAIN)); - } - - @Test - public void testCreateProduceProcessorsNormalWithView() { - findOperation("jsonWithView"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findJsonProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findJsonProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findJsonProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findJsonProcessorByViewClass(Object.class), - operationMeta.findProduceProcessor(MediaType.APPLICATION_JSON)); - Assertions.assertNull(operationMeta.findProduceProcessor(MediaType.TEXT_PLAIN)); - } - - @Test - public void testCreateProduceProcessorsNotSupported() { - findOperation("notSupport"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultJsonProcessor(), - operationMeta.findProduceProcessor(MediaType.APPLICATION_JSON)); - Assertions.assertNull(operationMeta.findProduceProcessor(MediaType.TEXT_PLAIN)); - } - - @Test - public void testCreateProduceProcessorsNotSupportedWithView() { - findOperation("notSupportWithView"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.findProduceProcessor(MediaType.APPLICATION_JSON)); - Assertions.assertNull(operationMeta.findProduceProcessor(MediaType.TEXT_PLAIN)); - } - - @Test - public void testCreateProduceProcessorsTextAndWildcard() { - findOperation("textPlain"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultPlainProcessor(), - operationMeta.ensureFindProduceProcessor(MediaType.WILDCARD)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultPlainProcessor(), - operationMeta.ensureFindProduceProcessor(MediaType.TEXT_PLAIN)); - Assertions.assertNull(operationMeta.ensureFindProduceProcessor(MediaType.APPLICATION_JSON)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultPlainProcessor(), - operationMeta.ensureFindProduceProcessor( - MediaType.APPLICATION_JSON + "," + MediaType.APPLICATION_XML + "," + MediaType.WILDCARD)); - } - - @Test - public void testCreateProduceProcessorsTextAndWildcardWithView() { - findOperation("textPlainWithView"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findPlainProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(MediaType.WILDCARD)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findPlainProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(MediaType.TEXT_PLAIN)); - Assertions.assertNull(operationMeta.ensureFindProduceProcessor(MediaType.APPLICATION_JSON)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findPlainProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor( - MediaType.APPLICATION_JSON + "," + MediaType.APPLICATION_XML + "," + MediaType.WILDCARD)); - } - - @Test - public void testCreateProduceProcessorsWithSemicolon() { - findOperation("textCharJsonChar"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultPlainProcessor(), - operationMeta.ensureFindProduceProcessor(MediaType.TEXT_PLAIN)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultJsonProcessor(), - operationMeta.ensureFindProduceProcessor(MediaType.APPLICATION_JSON)); - } - - @Test - public void testCreateProduceProcessorsWithSemicolonWithView() { - findOperation("textCharJsonCharWithView"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findPlainProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(MediaType.TEXT_PLAIN)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findJsonProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(MediaType.APPLICATION_JSON)); - } - - @Test - public void testEnsureFindProduceProcessorAcceptNotFound() { - findOperation("textCharJsonChar"); - Assertions.assertNull(operationMeta.ensureFindProduceProcessor("notSupport")); - } - - @Test - public void testEnsureFindProduceProcessorAcceptNotFoundWithView() { - findOperation("textCharJsonCharWithView"); - Assertions.assertNull(operationMeta.ensureFindProduceProcessor("notSupport")); - } - @Test public void generatesAbsolutePathWithRootBasePath() { findOperation("textCharJsonChar"); - MatcherAssert.assertThat(operationMeta.getAbsolutePath(), is("/textCharJsonChar/")); + MatcherAssert.assertThat(operationMeta.getAbsolutePath(), is("/textCharJsonChar")); } @Test public void generatesAbsolutePathWithNonRootBasePath() { findOperation("textCharJsonChar"); - Mockito.when(swagger.getBasePath()).thenReturn("/rest"); + Server server = Mockito.mock(Server.class); + Mockito.when(server.getUrl()).thenReturn("/rest"); + Mockito.when(swagger.getServers()).thenReturn(Arrays.asList(server)); RestOperationMeta restOperationMeta = new RestOperationMeta(); restOperationMeta.init(meta); - MatcherAssert.assertThat(restOperationMeta.getAbsolutePath(), is("/rest/textCharJsonChar/")); + MatcherAssert.assertThat(restOperationMeta.getAbsolutePath(), is("/rest/textCharJsonChar")); } @Test public void generatesAbsolutePathWithNullPath() { findOperation("textCharJsonChar"); - Mockito.when(swagger.getBasePath()).thenReturn(null); + Server server = Mockito.mock(Server.class); + Mockito.when(server.getUrl()).thenReturn(null); + Mockito.when(swagger.getServers()).thenReturn(Arrays.asList(server)); Mockito.when(meta.getOperationPath()).thenReturn(null); RestOperationMeta restOperationMeta = new RestOperationMeta(); restOperationMeta.init(meta); @@ -406,7 +242,9 @@ public void generatesAbsolutePathWithNullPath() { @Test public void generatesAbsolutePathWithEmptyPath() { findOperation("textCharJsonChar"); - Mockito.when(swagger.getBasePath()).thenReturn(""); + Server server = Mockito.mock(Server.class); + Mockito.when(server.getUrl()).thenReturn(""); + Mockito.when(swagger.getServers()).thenReturn(Arrays.asList(server)); Mockito.when(meta.getOperationPath()).thenReturn(""); RestOperationMeta restOperationMeta = new RestOperationMeta(); restOperationMeta.init(meta); @@ -417,7 +255,9 @@ public void generatesAbsolutePathWithEmptyPath() { @Test public void consecutiveSlashesAreRemoved() { findOperation("textCharJsonChar"); - Mockito.when(swagger.getBasePath()).thenReturn("//rest//"); + Server server = Mockito.mock(Server.class); + Mockito.when(server.getUrl()).thenReturn("//rest//"); + Mockito.when(swagger.getServers()).thenReturn(Arrays.asList(server)); Mockito.when(meta.getOperationPath()).thenReturn("//sayHi//"); RestOperationMeta restOperationMeta = new RestOperationMeta(); restOperationMeta.init(meta); diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriterTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriterTest.java index 5bd32b5933a..1aafc89fbb3 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriterTest.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriterTest.java @@ -26,11 +26,16 @@ import org.apache.servicecomb.common.rest.definition.RestParam; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; - -import io.swagger.models.parameters.QueryParameter; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; +import io.swagger.v3.oas.models.parameters.QueryParameter; public class QueryVarParamWriterTest { private static QueryVarParamWriter queryVarParamWriterCsv; @@ -39,24 +44,39 @@ public class QueryVarParamWriterTest { private static QueryVarParamWriter queryVarParamWriterDefault; + static Environment environment = Mockito.mock(Environment.class); + @BeforeAll public static void beforeClass() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) + .thenReturn(false); + QueryParameter parameter = new QueryParameter(); parameter.setName("q"); - parameter.setCollectionFormat("csv"); + parameter.setSchema(new Schema()); + parameter.setStyle(StyleEnum.FORM); + parameter.setExplode(false); queryVarParamWriterCsv = new QueryVarParamWriter( - new RestParam(parameter, String[].class)); + new RestParam(null, parameter, String[].class)); parameter = new QueryParameter(); parameter.setName("q"); - parameter.setCollectionFormat("multi"); + parameter.setSchema(new Schema()); + parameter.setStyle(StyleEnum.FORM); + parameter.setExplode(true); queryVarParamWriterMulti = new QueryVarParamWriter( - new RestParam(parameter, String[].class)); + new RestParam(null, parameter, String[].class)); parameter = new QueryParameter(); parameter.setName("q"); + parameter.setSchema(new Schema()); queryVarParamWriterDefault = new QueryVarParamWriter( - new RestParam(parameter, String[].class)); + new RestParam(null, parameter, String[].class)); } @Test diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilderTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilderTest.java index 2188f66cc08..bcff2ff1523 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilderTest.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilderTest.java @@ -23,14 +23,32 @@ import java.util.Map; import org.apache.servicecomb.common.rest.definition.RestParam; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; - -import io.swagger.models.parameters.Parameter; -import io.swagger.models.parameters.PathParameter; -import io.swagger.models.parameters.QueryParameter; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.PathParameter; +import io.swagger.v3.oas.models.parameters.QueryParameter; public class URLPathBuilderTest { + static Environment environment = Mockito.mock(Environment.class); + + @BeforeAll + public static void beforeClass() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) + .thenReturn(false); + } + @Test public void testNormal() throws Exception { Map paramMap = new LinkedHashMap<>(); @@ -117,7 +135,8 @@ private void addParam(String paramName, Type paramType, ParameterConstructor constructor, Map paramMap) { Parameter parameter = constructor.construct(); parameter.setName(paramName); - paramMap.put(paramName, new RestParam(parameter, paramType)); + parameter.setSchema(new Schema()); + paramMap.put(paramName, new RestParam(null, parameter, paramType)); } interface ParameterConstructor { diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/HttpClientFilterBaseForTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/HttpClientFilterBaseForTest.java deleted file mode 100644 index 7903b199e3e..00000000000 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/HttpClientFilterBaseForTest.java +++ /dev/null @@ -1,41 +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.servicecomb.common.rest.filter; - -import java.util.concurrent.CompletableFuture; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; -import org.apache.servicecomb.swagger.invocation.Response; - -public class HttpClientFilterBaseForTest implements HttpClientFilter { - @Override - public int getOrder() { - return 0; - } - - @Override - public CompletableFuture beforeSendRequestAsync(Invocation invocation, HttpServletRequestEx requestEx) { - return null; - } - - @Override - public Response afterReceiveResponse(Invocation invocation, HttpServletResponseEx responseEx) { - return null; - } -} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/HttpServerFilterBaseForTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/HttpServerFilterBaseForTest.java deleted file mode 100644 index 24ccd95a81d..00000000000 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/HttpServerFilterBaseForTest.java +++ /dev/null @@ -1,33 +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.servicecomb.common.rest.filter; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.swagger.invocation.Response; - -public class HttpServerFilterBaseForTest implements HttpServerFilter { - @Override - public int getOrder() { - return 0; - } - - @Override - public Response afterReceiveRequest(Invocation invocation, HttpServletRequestEx requestEx) { - return null; - } -} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/TestHttpClientFilter.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/TestHttpClientFilter.java deleted file mode 100644 index afc788b9213..00000000000 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/TestHttpClientFilter.java +++ /dev/null @@ -1,33 +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.servicecomb.common.rest.filter; - -import java.util.Arrays; -import java.util.concurrent.ExecutionException; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestHttpClientFilter { - @Test - public void asyncFailed() { - HttpClientFilter filter = new HttpClientFilterBaseForTest(); - HttpClientFilterBeforeSendRequestExecutor executor = - new HttpClientFilterBeforeSendRequestExecutor(Arrays.asList(filter), null, null); - Assertions.assertThrows(ExecutionException.class, () -> executor.run().get()); - } -} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/TestHttpServerFilter.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/TestHttpServerFilter.java deleted file mode 100644 index a27073f1722..00000000000 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/TestHttpServerFilter.java +++ /dev/null @@ -1,56 +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.servicecomb.common.rest.filter; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestHttpServerFilter { - - @Test - public void asyncSucc() throws InterruptedException, ExecutionException { - HttpServerFilter filter = new HttpServerFilterBaseForTest(); - - CompletableFuture future = filter.beforeSendResponseAsync(null, null); - Assertions.assertNull(future.get()); - } - - @Test - public void asyncFailed() throws InterruptedException, ExecutionException { - HttpServerFilter filter = new HttpServerFilterBaseForTest() { - @Override - public CompletableFuture beforeSendResponseAsync(Invocation invocation, HttpServletResponseEx responseEx) { - CompletableFuture result = new CompletableFuture<>(); - result.completeExceptionally(new RuntimeExceptionWithoutStackTrace()); - return result; - } - }; - - ExecutionException exception = Assertions.assertThrows(ExecutionException.class, - () -> { - CompletableFuture future = filter.beforeSendResponseAsync(null, null); - future.get(); - }); - Assertions.assertTrue(exception.getCause() instanceof RuntimeExceptionWithoutStackTrace); - } -} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/TestHttpServerFilterBeforeSendResponseExecutor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/TestHttpServerFilterBeforeSendResponseExecutor.java deleted file mode 100644 index 169c2fda5dd..00000000000 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/TestHttpServerFilterBeforeSendResponseExecutor.java +++ /dev/null @@ -1,71 +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.servicecomb.common.rest.filter; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; -import org.junit.jupiter.api.Assertions; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; - -public class TestHttpServerFilterBeforeSendResponseExecutor { - @Mock - Invocation invocation; - - @Mock - HttpServletResponseEx responseEx; - - final List httpServerFilters = new ArrayList<>(); - - final HttpServerFilterBeforeSendResponseExecutor executor = - new HttpServerFilterBeforeSendResponseExecutor(httpServerFilters, invocation, responseEx); - - @BeforeEach - public void setup() { - httpServerFilters.add(new HttpServerFilterBaseForTest()); - } - - @Test - public void runSucc() throws InterruptedException, ExecutionException { - CompletableFuture result = executor.run(); - - Assertions.assertNull(result.get()); - } - - @Test - public void runFail() throws InterruptedException, ExecutionException { - httpServerFilters.add(new HttpServerFilterBaseForTest() { - @Override - public CompletableFuture beforeSendResponseAsync(Invocation invocation, HttpServletResponseEx responseEx) { - throw new RuntimeExceptionWithoutStackTrace(); - } - }); - - CompletableFuture result = executor.run(); - - ExecutionException exception = Assertions.assertThrows(ExecutionException.class, result::get); - Assertions.assertTrue(exception.getCause() instanceof RuntimeExceptionWithoutStackTrace); - } -} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java index b4093a1cadc..0decec28bd9 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java @@ -19,19 +19,17 @@ import static com.google.common.net.HttpHeaders.CONTENT_LENGTH; import static com.google.common.net.HttpHeaders.TRANSFER_ENCODING; -import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; -import static org.assertj.core.api.Assertions.assertThat; +import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; +import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; +import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; +import java.io.ByteArrayInputStream; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import javax.servlet.http.Part; - import org.apache.servicecomb.common.rest.HttpTransportContext; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.produce.ProduceJsonProcessor; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; -import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; @@ -41,23 +39,33 @@ import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.invocation.InvocationFactory; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.part.InputStreamPart; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.springframework.core.env.Environment; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; import io.vertx.core.MultiMap; -import io.vertx.core.json.Json; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.MediaType; public class RestServerCodecFilterTest { final RestServerCodecFilter codecFilter = new RestServerCodecFilter(); @@ -82,27 +90,37 @@ public class RestServerCodecFilterTest { final MultiMap headers = MultiMap.caseInsensitiveMultiMap(); - final FilterNode nextNode = new FilterNode((invocation, next) -> { - Response response = Response.ok("ok"); - response.setHeaders(headers); - return CompletableFuture.completedFuture(response); + final Part part = new InputStreamPart("ok", new ByteArrayInputStream(new byte[] {1, 2})); + + final FilterNode nextNode = new FilterNode(new AbstractFilter() { + @Override + public String getName() { + return "f2"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + Response response = Response.ok(part); + response.setHeaders(headers); + return CompletableFuture.completedFuture(response); + } }); static SCBEngine engine; @BeforeAll public static void beforeClass() { - ArchaiusUtils.resetConfig(); - ConfigUtil.installDynamicConfig(); - - engine = SCBBootstrap.createSCBEngineForTest(); + Environment environment = Mockito.mock(Environment.class); + engine = SCBBootstrap.createSCBEngineForTest(environment); + engine.setEnvironment(environment); + Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); engine.setStatus(SCBStatus.UP); } @AfterAll public static void afterClass() { engine.destroy(); - ArchaiusUtils.resetConfig(); } @BeforeEach @@ -110,14 +128,15 @@ public void setUp() { Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); - Mockito.when(transportContext.getProduceProcessor()).thenReturn(Mockito.mock(ProduceJsonProcessor.class)); invocation = Mockito.spy(InvocationFactory.forProvider(endpoint, operationMeta, null)); + Mockito.when(responseEx.sendPart(part)).thenReturn(CompletableFuture.completedFuture(null)); } private void mockDecodeRequestFail() { Mockito.when(invocation.getTransportContext()).thenReturn(transportContext); Mockito.when(transportContext.getResponseEx()).thenReturn(responseEx); - Mockito.when(invocation.getRequestEx()).thenThrow(new RuntimeExceptionWithoutStackTrace("mock encode request failed")); + Mockito.when(invocation.getRequestEx()) + .thenThrow(new RuntimeExceptionWithoutStackTrace("mock encode request failed")); } @Test @@ -135,20 +154,30 @@ public void should_convert_exception_to_response_when_decode_request_failed() throws ExecutionException, InterruptedException { mockDecodeRequestFail(); Mockito.when(invocation.findResponseType(INTERNAL_SERVER_ERROR.getStatusCode())) - .thenReturn(TypeFactory.defaultInstance().constructType(String.class)); - - Response response = codecFilter.onFilter(invocation, nextNode).get(); + .thenReturn(TypeFactory.defaultInstance().constructType(String.class)); - assertThat(response.getStatus()).isEqualTo(INTERNAL_SERVER_ERROR); - assertThat(Json.encode(response.getResult())). - isIn("{\"code\":\"SCB.50000000\",\"message\":\"mock encode request failed\"}", - "{\"message\":\"mock encode request failed\",\"code\":\"SCB.50000000\"}"); + Assertions.assertThrows(ExecutionException.class, + () -> codecFilter.onFilter(invocation, nextNode).get()); } private void success_invocation() throws InterruptedException, ExecutionException { Mockito.when(invocation.getTransportContext()).thenReturn(transportContext); + HttpServletRequestEx requestExt = Mockito.mock(HttpServletRequestEx.class); + Mockito.when(invocation.getRequestEx()).thenReturn(requestExt); + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + Operation swaggerOperation = Mockito.mock(Operation.class); + Mockito.when(operationMeta.getSwaggerOperation()).thenReturn(swaggerOperation); + ApiResponses apiResponses = Mockito.mock(ApiResponses.class); + Mockito.when(swaggerOperation.getResponses()).thenReturn(apiResponses); + ApiResponse apiResponse = Mockito.mock(ApiResponse.class); + Mockito.when(apiResponses.get("200")).thenReturn(apiResponse); + Content content = new Content(); + content.addMediaType(MediaType.APPLICATION_JSON, new io.swagger.v3.oas.models.media.MediaType()); + content.get(MediaType.APPLICATION_JSON).setSchema(new StringSchema()); + Mockito.when(apiResponse.getContent()).thenReturn(content); Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(restOperationMeta); - Mockito.when(invocation.findResponseType(INTERNAL_SERVER_ERROR.getStatusCode())).thenReturn(TypeFactory.defaultInstance().constructType(String.class)); + Mockito.when(invocation.findResponseType(INTERNAL_SERVER_ERROR.getStatusCode())) + .thenReturn(TypeFactory.defaultInstance().constructType(String.class)); JavaType javaType = Mockito.mock(JavaType.class); Mockito.when(invocationRuntimeType.findResponseType(200)).thenReturn(javaType); Mockito.when(javaType.getRawClass()).thenAnswer(invocationOnMock -> Part.class); @@ -163,7 +192,7 @@ public void should_encode_response_header() throws ExecutionException, Interrupt headers.add("h1", "v1"); success_invocation(); - Mockito.verify(responseEx).addHeader("h1", "v1"); + Mockito.verify(responseEx).addHeader("h1", "v1"); } @Test diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/TestServerRestArgsFilter.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/TestServerRestArgsFilter.java deleted file mode 100644 index b1cef1a617f..00000000000 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/TestServerRestArgsFilter.java +++ /dev/null @@ -1,66 +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.servicecomb.common.rest.filter.inner; - -import javax.servlet.http.Part; - -import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.response.ResponsesMeta; -import org.junit.jupiter.api.Assertions; - -import com.fasterxml.jackson.databind.type.TypeFactory; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - - -public class TestServerRestArgsFilter { - - final Invocation invocation = Mockito.mock(Invocation.class); - - final HttpServletResponseEx responseEx = Mockito.mock(HttpServletResponseEx.class); - - final Response response = Mockito.mock(Response.class); - - final Part part = Mockito.mock(Part.class); - - boolean invokedSendPart; - - final ServerRestArgsFilter filter = new ServerRestArgsFilter(); - - @Test - public void asyncBeforeSendResponse_part() { - ResponsesMeta responsesMeta = new ResponsesMeta(); - responsesMeta.getResponseMap().put(202, RestObjectMapperFactory.getRestObjectMapper().constructType(Part.class)); - - Mockito.when(responseEx.getAttribute(RestConst.INVOCATION_HANDLER_RESPONSE)).thenReturn(response); - Mockito.when(response.getResult()).thenReturn(part); - Mockito.when(response.getStatusCode()).thenReturn(202); - Mockito.when(invocation.findResponseType(202)).thenReturn(TypeFactory.defaultInstance().constructType(Part.class)); - - Mockito.doAnswer(invocationOnMock -> { - invokedSendPart = true; - return null; - }).when(responseEx).sendPart(part); - - Assertions.assertNull(filter.beforeSendResponseAsync(invocation, responseEx)); - Assertions.assertTrue(invokedSendPart); - } -} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestMicroservicePaths.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestMicroservicePaths.java index e173ecf12ef..d99fb3b04f1 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestMicroservicePaths.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestMicroservicePaths.java @@ -17,39 +17,65 @@ package org.apache.servicecomb.common.rest.locator; +import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; +import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.common.rest.RestEngineSchemaListener; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; -import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; +import org.apache.servicecomb.core.executor.ExecutorManager; +import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.springframework.core.env.Environment; public class TestMicroservicePaths { - static SCBEngine scbEngine; + SCBEngine scbEngine; - static MicroservicePaths paths; + MicroservicePaths paths; - @BeforeAll - public static void setup() { - ConfigUtil.installDynamicConfig(); - scbEngine = SCBBootstrap.createSCBEngineForTest() - .addProducerMeta("sid1", new TestPathSchema()) + @BeforeEach + public void setup() { + Environment environment = Mockito.mock(Environment.class); + scbEngine = SCBBootstrap.createSCBEngineForTest(environment); + + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)) + .thenReturn(BootStrapProperties.DEFAULT_APPLICATION); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)) + .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_NAME); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_ENVIRONMENT)) + .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_ENVIRONMENT); + Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) + .thenReturn(false); + List listeners = new ArrayList<>(); + listeners.add(new RestEngineSchemaListener()); + ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); + TransportManager transportManager = Mockito.mock(TransportManager.class); + scbEngine.setTransportManager(transportManager); + scbEngine.setExecutorManager(executorManager); + scbEngine.setBootListeners(listeners); + scbEngine.addProducerMeta("sid1", new TestPathSchema()) .run(); ServicePathManager spm = ServicePathManager.getServicePathManager(scbEngine.getProducerMicroserviceMeta()); paths = spm.producerPaths; } - @AfterAll - public static void teardown() { + @AfterEach + public void teardown() { scbEngine.destroy(); - ArchaiusUtils.resetConfig(); } @Test @@ -68,33 +94,16 @@ public void testAddResourceStaticDuplicatedHttpMethod() { Mockito.when(staticResPost.getAbsolutePath()).thenReturn("/static/"); Mockito.when(staticResPost.isAbsoluteStaticPath()).thenReturn(true); - ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, - () -> paths.addResource(staticResPost)); + () -> paths.addResource(staticResPost)); Assertions.assertEquals("operation with url /static/, method POST is duplicated.", exception.getMessage()); } @Test public void dynamicPath() { - Assertions.assertEquals("dynamicExId", paths.getDynamicPathOperationList().get(0).getOperationMeta().getOperationId()); - Assertions.assertEquals("dynamicId", paths.getDynamicPathOperationList().get(1).getOperationMeta().getOperationId()); - } - - @Test - public void testPrintPaths() { - try (LogCollector collector = new LogCollector()) { - paths.printPaths(); - - StringBuilder sb = new StringBuilder(); - collector.getEvents() - .forEach(e -> sb.append(e.getMessage().getFormattedMessage()).append("\n")); - Assertions.assertEquals( - "Swagger mapped \"{[/static/], method=[POST], produces=[application/json]}\" onto public void org.apache.servicecomb.common.rest.locator.TestPathSchema.postStatic()\n" - + "Swagger mapped \"{[/static/], method=[GET], produces=[application/json]}\" onto public void org.apache.servicecomb.common.rest.locator.TestPathSchema.getStatic()\n" - + "Swagger mapped \"{[/staticEx/], method=[GET], produces=[application/json]}\" onto public void org.apache.servicecomb.common.rest.locator.TestPathSchema.getStaticEx()\n" - + "Swagger mapped \"{[/dynamicEx/{id}/], method=[GET], produces=[application/json]}\" onto public void org.apache.servicecomb.common.rest.locator.TestPathSchema.dynamicExId(java.lang.String)\n" - + "Swagger mapped \"{[/dynamic/{id}/], method=[GET], produces=[application/json]}\" onto public void org.apache.servicecomb.common.rest.locator.TestPathSchema.dynamicId(java.lang.String)\n", - sb.toString()); - } + Assertions.assertEquals("dynamicExId", + paths.getDynamicPathOperationList().get(0).getOperationMeta().getOperationId()); + Assertions.assertEquals("dynamicId", + paths.getDynamicPathOperationList().get(1).getOperationMeta().getOperationId()); } } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestPathSchema.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestPathSchema.java index 66e7f4a8a66..b0de6caf78b 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestPathSchema.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestPathSchema.java @@ -16,10 +16,10 @@ */ package org.apache.servicecomb.common.rest.locator; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; @Path("/") public class TestPathSchema { diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestServicePathManager.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestServicePathManager.java index d7daf5a5577..84ae22015ae 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestServicePathManager.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestServicePathManager.java @@ -17,34 +17,70 @@ package org.apache.servicecomb.common.rest.locator; -import org.apache.servicecomb.config.ConfigUtil; +import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; +import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.common.rest.RestEngineSchemaListener; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; +import org.apache.servicecomb.core.executor.ExecutorManager; +import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.apache.servicecomb.registry.definition.DefinitionConst; 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 org.mockito.Mockito; +import org.springframework.core.env.Environment; public class TestServicePathManager { + SCBEngine scbEngine; + + Environment environment; + @BeforeEach public void setUp() { - ConfigUtil.installDynamicConfig(); + environment = Mockito.mock(Environment.class); + scbEngine = SCBBootstrap.createSCBEngineForTest(environment); + + Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)) + .thenReturn(BootStrapProperties.DEFAULT_APPLICATION); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)) + .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_NAME); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_ENVIRONMENT)) + .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_ENVIRONMENT); } @AfterEach public void tearDown() { - ArchaiusUtils.resetConfig(); + scbEngine.destroy(); ClassLoaderScopeContext.clearClassLoaderScopeProperty(); } @Test public void testBuildProducerPathsNoPrefix() { - SCBEngine scbEngine = SCBBootstrap.createSCBEngineForTest() - .addProducerMeta("sid1", new TestPathSchema()) + ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); + TransportManager transportManager = Mockito.mock(TransportManager.class); + scbEngine.setTransportManager(transportManager); + scbEngine.setExecutorManager(executorManager); + List listeners = new ArrayList<>(); + listeners.add(new RestEngineSchemaListener()); + scbEngine.setBootListeners(listeners); + scbEngine.addProducerMeta("sid1", new TestPathSchema()) .run(); + ServicePathManager spm = ServicePathManager.getServicePathManager(scbEngine.getProducerMicroserviceMeta()); Assertions.assertSame(spm.producerPaths, spm.swaggerPaths); @@ -56,9 +92,20 @@ public void testBuildProducerPathsNoPrefix() { public void testBuildProducerPathsHasPrefix() { ClassLoaderScopeContext.setClassLoaderScopeProperty(DefinitionConst.URL_PREFIX, "/root/rest"); - SCBEngine scbEngine = SCBBootstrap.createSCBEngineForTest() - .addProducerMeta("sid1", new TestPathSchema()) + Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); + Mockito.when(environment.getProperty(DefinitionConst.REGISTER_URL_PREFIX, boolean.class, false)).thenReturn(false); + + ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); + TransportManager transportManager = Mockito.mock(TransportManager.class); + scbEngine.setTransportManager(transportManager); + scbEngine.setExecutorManager(executorManager); + List listeners = new ArrayList<>(); + listeners.add(new RestEngineSchemaListener()); + scbEngine.setBootListeners(listeners); + scbEngine.addProducerMeta("sid1", new TestPathSchema()) .run(); + ServicePathManager spm = ServicePathManager.getServicePathManager(scbEngine.getProducerMicroserviceMeta()); // all locate should be success diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/resource/TestClassPathStaticResourceHandler.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/resource/TestClassPathStaticResourceHandler.java index 6923ac42006..f95468f4f38 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/resource/TestClassPathStaticResourceHandler.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/resource/TestClassPathStaticResourceHandler.java @@ -20,10 +20,6 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; -import javax.servlet.http.Part; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.Response.Status; - import org.apache.commons.io.IOUtils; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; @@ -35,6 +31,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response.Status; + public class TestClassPathStaticResourceHandler { static ClassPathStaticResourceHandler handler = new ClassPathStaticResourceHandler(); @@ -49,7 +49,7 @@ public void normal() throws IOException { Part part = response.getResult(); try (InputStream is = part.getInputStream()) { - Assertions.assertTrue(IOUtils.toString(is, StandardCharsets.UTF_8).endsWith("")); + Assertions.assertTrue(IOUtils.toString(is, StandardCharsets.UTF_8).trim().endsWith("")); } Assertions.assertEquals("text/html", part.getContentType()); Assertions.assertEquals("text/html", response.getHeader(HttpHeaders.CONTENT_TYPE)); @@ -84,7 +84,7 @@ public void attack() { public void readContentFailed() throws IOException { handler = Mockito.spy(TestClassPathStaticResourceHandler.handler); Mockito.when(handler.findResource("web-root/index.html")) - .thenThrow(new RuntimeExceptionWithoutStackTrace("read content failed.")); + .thenThrow(new RuntimeExceptionWithoutStackTrace("read content failed.")); try (LogCollector logCollector = new LogCollector()) { Response response = handler.handle("index.html"); diff --git a/common/common-rest/src/test/resources/web-root/index.html b/common/common-rest/src/test/resources/web-root/index.html index 6aed06f6ed3..8fe33dc3cfa 100644 --- a/common/common-rest/src/test/resources/web-root/index.html +++ b/common/common-rest/src/test/resources/web-root/index.html @@ -14,4 +14,4 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - \ No newline at end of file + diff --git a/common/pom.xml b/common/pom.xml index 7da3ac4f815..c39c89ff4ce 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,14 +21,13 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default common Java Chassis::Common pom - common-protobuf common-rest common-access-log diff --git a/core/pom.xml b/core/pom.xml index d0c3933a702..af73c36dff9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -21,17 +21,13 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default java-chassis-core Java Chassis::Core - - org.apache.servicecomb - deployment - org.apache.servicecomb foundation-vertx @@ -44,6 +40,10 @@ org.apache.servicecomb swagger-invocation-core + + org.apache.servicecomb + swagger-generator-core + io.zipkin.brave brave diff --git a/core/src/main/java/org/apache/servicecomb/core/BootListener.java b/core/src/main/java/org/apache/servicecomb/core/BootListener.java index 247bfbd9ba7..ea8314e2e1d 100644 --- a/core/src/main/java/org/apache/servicecomb/core/BootListener.java +++ b/core/src/main/java/org/apache/servicecomb/core/BootListener.java @@ -17,7 +17,9 @@ package org.apache.servicecomb.core; -public interface BootListener { +import org.springframework.core.Ordered; + +public interface BootListener extends Ordered { enum EventType { BEFORE_FILTER, AFTER_FILTER, @@ -65,6 +67,7 @@ public BootEvent setEventType(EventType eventType) { } } + @Override default int getOrder() { return 0; } @@ -112,14 +115,6 @@ default void onBootEvent(BootEvent event) { } } - default void onBeforeHandler(BootEvent event) { - - } - - default void onAfterHandler(BootEvent event) { - - } - default void onBeforeFilter(BootEvent event) { } diff --git a/core/src/main/java/org/apache/servicecomb/core/ConfigurationSpringInitializer.java b/core/src/main/java/org/apache/servicecomb/core/ConfigurationSpringInitializer.java deleted file mode 100644 index 791c441f761..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/ConfigurationSpringInitializer.java +++ /dev/null @@ -1,355 +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.servicecomb.core; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.config.ConfigMapping; -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.config.YAMLUtil; -import org.apache.servicecomb.config.archaius.sources.MicroserviceConfigLoader; -import org.apache.servicecomb.config.event.DynamicConfigurationChangedEvent; -import org.apache.servicecomb.config.event.RefreshGovernanceConfigurationEvent; -import org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource; -import org.apache.servicecomb.foundation.bootstrap.BootStrapService; -import org.apache.servicecomb.foundation.common.event.EventManager; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.EnvironmentAware; -import org.springframework.context.annotation.Conditional; -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.core.Ordered; -import org.springframework.core.env.CompositePropertySource; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.EnumerablePropertySource; -import org.springframework.core.env.Environment; -import org.springframework.core.env.MapPropertySource; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertySource; - - -import com.google.common.eventbus.Subscribe; -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.WatchedUpdateResult; - -/** - * Adapt spring PropertySource and Archaius Configuration - * spring vs archaius - * (add) | dynamic(configcenter) - * system property | system property - * environment | environment - * *properties/*.yml | (add) - * (add) | microservice.yaml - * - * add dynamic configuration, microserive.yaml to spring, add *properties/*.yml to archaius - * - * NOTICE: we are not duplicate spring system property and environment property source, this will cause some problem - * related to precedence of a KEY-VAlUE. That is cse.test in dynamic config may not override servicecomb.test in yml. - * Users need to use the same key as what is in config file to override. - */ -public class ConfigurationSpringInitializer extends PropertySourcesPlaceholderConfigurer implements EnvironmentAware { - private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationSpringInitializer.class); - - public static final String EXTRA_CONFIG_SOURCE_PREFIX = "extraConfig-"; - - public static final String MICROSERVICE_PROPERTY_SOURCE_NAME = "microservice.yaml"; - - public static final String MAPPING_PROPERTY_SOURCE_NAME = "mapping.yaml"; - - public static final String EXTERNAL_INIT = "scb-config-external-init"; - - public static void setExternalInit(boolean value) { - System.setProperty(EXTERNAL_INIT, String.valueOf(value)); - } - - public static boolean isExternalInit() { - return Boolean.getBoolean(EXTERNAL_INIT); - } - - private final List bootStrapServices = SPIServiceUtils.getSortedService(BootStrapService.class); - - private final Map dynamicData = new ConcurrentHashMap<>(); - - private ConfigCenterConfigurationSource configCenterConfigurationSource; - - public ConfigurationSpringInitializer() { - setOrder(Ordered.LOWEST_PRECEDENCE / 2); - setIgnoreUnresolvablePlaceholders(true); - } - - /** - * Get configurations from Spring, merge them into the configurations of ServiceComb. - * @param environment From which to get the extra config. - */ - @Override - public void setEnvironment(Environment environment) { - super.setEnvironment(environment); - if (isExternalInit()) { - return; - } - - syncFromSpring(environment); - syncToSpring(environment); - - startupBootStrapService(environment); - - // watch configuration changes - EventManager.register(this); - configCenterConfigurationSource = ConfigUtil.installDynamicConfig(); - addDynamicConfigurationToSpring(environment, configCenterConfigurationSource); - } - - @Subscribe - public void onConfigurationDataChanged(DynamicConfigurationChangedEvent event) { - try { - WatchedUpdateResult data = event.getEvent(); - if (data.getDeleted() != null) { - data.getDeleted().forEach((k, v) -> dynamicData.remove(k)); - } - if (data.getAdded() != null) { - dynamicData.putAll(data.getAdded()); - } - if (data.getChanged() != null) { - dynamicData.putAll(data.getChanged()); - } - } catch (Exception e) { - LOGGER.error("", e); - } - EventManager.post(new RefreshGovernanceConfigurationEvent(event.getEvent())); - } - - private void syncFromSpring(Environment environment) { - String environmentName = generateNameForEnvironment(environment); - LOGGER.info("Environment received, will get configurations from [{}].", environmentName); - - Map extraConfig = getAllProperties(environment); - ConfigUtil.addExtraConfig(EXTRA_CONFIG_SOURCE_PREFIX + environmentName, extraConfig); - } - - public static void syncToSpring(Environment environment) { - if (isExternalInit()) { - return; - } - - addMicroserviceYAMLToSpring(environment); - addMappingToSpring(environment); - } - - private void startupBootStrapService(Environment environment) { - bootStrapServices.forEach(bootStrapService -> bootStrapService.startup(environment)); - } - - /** - * make springboot have a change to add microservice.yaml source earlier
- * to affect {@link Conditional} - * @param environment environment - */ - private static void addMicroserviceYAMLToSpring(Environment environment) { - if (!(environment instanceof ConfigurableEnvironment)) { - return; - } - - MutablePropertySources propertySources = ((ConfigurableEnvironment) environment).getPropertySources(); - if (propertySources.contains(MICROSERVICE_PROPERTY_SOURCE_NAME)) { - return; - } - - propertySources.addLast(new EnumerablePropertySource(MICROSERVICE_PROPERTY_SOURCE_NAME) { - private final Map values = new HashMap<>(); - - private final String[] propertyNames; - - { - MicroserviceConfigLoader loader = new MicroserviceConfigLoader(); - loader.loadAndSort(); - - loader.getConfigModels() - .forEach(configModel -> values.putAll(YAMLUtil.retrieveItems("", configModel.getConfig()))); - - propertyNames = values.keySet().toArray(new String[0]); - } - - @Override - public String[] getPropertyNames() { - return propertyNames; - } - - @SuppressWarnings("unchecked") - @Override - public Object getProperty(String name) { - Object value = this.values.get(name); - - // spring will not resolve nested placeholder of list, so try to fix the problem - if (value instanceof List) { - value = ((List) value).stream() - .filter(item -> item instanceof String) - .map(item -> environment.resolvePlaceholders((String) item)) - .collect(Collectors.toList()); - } - return value; - } - }); - } - - private static void addMappingToSpring(Environment environment) { - if (!(environment instanceof ConfigurableEnvironment)) { - return; - } - - MutablePropertySources propertySources = ((ConfigurableEnvironment) environment).getPropertySources(); - if (propertySources.contains(MAPPING_PROPERTY_SOURCE_NAME)) { - return; - } - - Map mappings = ConfigMapping.getConvertedMap(environment); - propertySources.addFirst(new MapPropertySource(MAPPING_PROPERTY_SOURCE_NAME, mappings)); - } - - private void addDynamicConfigurationToSpring(Environment environment, - ConfigCenterConfigurationSource configCenterConfigurationSource) { - if (!(environment instanceof ConfigurableEnvironment)) { - return; - } - ConfigurableEnvironment ce = (ConfigurableEnvironment) environment; - if (configCenterConfigurationSource == null) { - return; - } - try { - ce.getPropertySources().addFirst(new MapPropertySource("dynamic-source", dynamicData)); - } catch (Exception e) { - if (DynamicPropertyFactory.getInstance().getBooleanProperty(Const.PRINT_SENSITIVE_ERROR_MESSAGE, - false).get()) { - LOGGER.warn("set up spring property source failed.", e); - } else { - LOGGER.warn("set up spring property source failed. msg: {}", e.getMessage()); - } - } - } - - @Override - protected Properties mergeProperties() throws IOException { - Properties properties = super.mergeProperties(); - if (isExternalInit()) { - return properties; - } - - AbstractConfiguration config = ConfigurationManager.getConfigInstance(); - Iterator iterator = config.getKeys(); - while (iterator.hasNext()) { - String key = iterator.next(); - Object value = config.getProperty(key); - properties.put(key, value); - } - return properties; - } - - /** - * Try to get a name for identifying the environment. - * @param environment the target that the name is generated for. - * @return The generated name for the environment. - */ - private String generateNameForEnvironment(Environment environment) { - String environmentName = environment.getProperty("spring.config.name"); - if (!StringUtils.isEmpty(environmentName)) { - return environmentName; - } - - environmentName = environment.getProperty("spring.application.name"); - if (!StringUtils.isEmpty(environmentName)) { - return environmentName; - } - - return environment.getClass().getName() + "@" + environment.hashCode(); - } - - /** - * Traversal all {@link PropertySource} of {@link ConfigurableEnvironment}, and try to get all properties. - */ - private Map getAllProperties(Environment environment) { - Map configFromSpringBoot = new HashMap<>(); - - if (!(environment instanceof ConfigurableEnvironment)) { - return configFromSpringBoot; - } - - ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; - - if (ignoreResolveFailure()) { - configurableEnvironment.setIgnoreUnresolvableNestedPlaceholders(true); - } - - for (PropertySource propertySource : configurableEnvironment.getPropertySources()) { - if (MICROSERVICE_PROPERTY_SOURCE_NAME.equals(propertySource.getName()) - || MAPPING_PROPERTY_SOURCE_NAME.equals(propertySource.getName())) { - continue; - } - getProperties(configurableEnvironment, propertySource, configFromSpringBoot); - } - return configFromSpringBoot; - } - - /** - * Get property names from {@link EnumerablePropertySource}, and get property value from {@link ConfigurableEnvironment#getProperty(String)} - */ - private void getProperties(ConfigurableEnvironment environment, PropertySource propertySource, - Map configFromSpringBoot) { - if (propertySource instanceof CompositePropertySource) { - // recursively get EnumerablePropertySource - CompositePropertySource compositePropertySource = (CompositePropertySource) propertySource; - compositePropertySource.getPropertySources().forEach(ps -> getProperties(environment, ps, configFromSpringBoot)); - return; - } - if (propertySource instanceof EnumerablePropertySource) { - EnumerablePropertySource enumerablePropertySource = (EnumerablePropertySource) propertySource; - for (String propertyName : enumerablePropertySource.getPropertyNames()) { - try { - Object propertyValue = environment.getProperty(propertyName, Object.class); - if (propertyValue == null) { - LOGGER.error("The value of a configuration item is null, please check whether there is any impact, config item key: {}", propertyName); - continue; - } - configFromSpringBoot.put(propertyName, propertyValue); - } catch (Exception e) { - throw new RuntimeException( - "set up spring property source failed.If you still want to start up the application and ignore errors, you can set servicecomb.config.ignoreResolveFailure to true.", - e); - } - } - return; - } - - LOGGER.debug("a none EnumerablePropertySource is ignored, propertySourceName = [{}]", propertySource.getName()); - } - - private boolean ignoreResolveFailure() { - return ConfigUtil - .createLocalConfig() - .getBoolean("servicecomb.config.ignoreResolveFailure", false); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/Const.java b/core/src/main/java/org/apache/servicecomb/core/Const.java deleted file mode 100644 index 03c751def9e..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/Const.java +++ /dev/null @@ -1,58 +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.servicecomb.core; - -import org.apache.servicecomb.registry.definition.DefinitionConst; - -public final class Const { - private Const() { - } - - public static final String CSE_CONTEXT = "x-cse-context"; - - public static final String RESTFUL = "rest"; - - public static final String HIGHWAY = "highway"; - - public static final String ANY_TRANSPORT = ""; - - public static final String VERSION_RULE_LATEST = DefinitionConst.VERSION_RULE_LATEST; - - public static final String DEFAULT_VERSION_RULE = DefinitionConst.VERSION_RULE_ALL; - - public static final String PRODUCER_OPERATION = "producer-operation"; - - public static final String CONSUMER_OPERATION = "consumer-operation"; - - public static final String SRC_MICROSERVICE = "x-cse-src-microservice"; - - public static final String SRC_SERVICE_ID = "x-src-serviceId"; - - public static final String SRC_INSTANCE_ID = "x-src-instanceId"; - - public static final String TARGET_MICROSERVICE = "x-cse-target-microservice"; - - public static final String REMOTE_ADDRESS = "x-cse-remote-address"; - - public static final String AUTH_TOKEN = "x-cse-auth-rsatoken"; - - public static final String TRACE_ID_NAME = "X-B3-TraceId"; - - // controlling whether to print stack information with sensitive errors - public static final String PRINT_SENSITIVE_ERROR_MESSAGE = "servicecomb.error.printSensitiveErrorMessage"; -} diff --git a/core/src/main/java/org/apache/servicecomb/core/ConsumerProvider.java b/core/src/main/java/org/apache/servicecomb/core/ConsumerProvider.java deleted file mode 100644 index b4b69a3cc51..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/ConsumerProvider.java +++ /dev/null @@ -1,28 +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.servicecomb.core; - -/** - * consumer端对业务的接口,不同场景是完全不同的 - * 所以这里只定义consumer对core的接口 - */ -public interface ConsumerProvider { - String getName(); - - void init(); -} diff --git a/core/src/main/java/org/apache/servicecomb/core/CoreConst.java b/core/src/main/java/org/apache/servicecomb/core/CoreConst.java new file mode 100644 index 00000000000..37c34b9c96d --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/CoreConst.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.core; + +import org.apache.servicecomb.registry.definition.DefinitionConst; + +public final class CoreConst { + private CoreConst() { + } + + public static final String CSE_CONTEXT = "x-cse-context"; + + public static final String TRANSPORT_NAME = "x-transport-name"; + + public static final String RESTFUL = "rest"; + + public static final String HIGHWAY = "highway"; + + public static final String WEBSOCKET = "websocket"; + + public static final String ANY_TRANSPORT = ""; + + public static final String VERSION_RULE_LATEST = DefinitionConst.VERSION_RULE_LATEST; + + public static final String DEFAULT_VERSION_RULE = DefinitionConst.VERSION_RULE_ALL; + + public static final String PRODUCER_OPERATION = "producer-operation"; + + public static final String CONSUMER_OPERATION = "consumer-operation"; + + public static final String SRC_MICROSERVICE = "x-cse-src-microservice"; + + public static final String SRC_SERVICE_ID = "x-src-serviceId"; + + public static final String SRC_INSTANCE_ID = "x-src-instanceId"; + + public static final String TARGET_MICROSERVICE = "x-cse-target-microservice"; + + public static final String REMOTE_ADDRESS = "x-cse-remote-address"; + + public static final String AUTH_TOKEN = "x-cse-auth-rsatoken"; + + public static final String TRACE_ID_NAME = "X-B3-TraceId"; + + // controlling whether to print stack information with sensitive errors + public static final String PRINT_SENSITIVE_ERROR_MESSAGE = "servicecomb.error.printSensitiveErrorMessage"; + + public static final String SWAGGER_EXPORT_ENABLED = "servicecomb.swagger.export.enabled"; + + public static final String SWAGGER_DIRECTORY = "servicecomb.swagger.export.directory"; +} diff --git a/core/src/main/java/org/apache/servicecomb/core/Endpoint.java b/core/src/main/java/org/apache/servicecomb/core/Endpoint.java index 62bcd7c35c3..01f6f1129d5 100644 --- a/core/src/main/java/org/apache/servicecomb/core/Endpoint.java +++ b/core/src/main/java/org/apache/servicecomb/core/Endpoint.java @@ -17,35 +17,36 @@ package org.apache.servicecomb.core; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; /** - * Endpoint : 表示一个Endpoint。Transport识别其内部格式. + * Internal representation for microservice instance address. */ public class Endpoint { - // 格式:rest://192.168.1.1:8080 + // Registry address: http://192.168.1.1:8080 // see: http://www.ietf.org/rfc/rfc2396.txt private final String endpoint; private final Transport transport; - private final MicroserviceInstance instance; + private final DiscoveryInstance instance; - // 内部格式, 只有Transport能够认识 + // Internal address format recognized by Transport private final Object address; public Endpoint(Transport transport, String endpoint) { this(transport, endpoint, null); } - public Endpoint(Transport transport, String endpoint, MicroserviceInstance instance) { + public Endpoint(Transport transport, String endpoint, DiscoveryInstance instance) { this.transport = transport; this.endpoint = endpoint; this.instance = instance; this.address = transport.parseAddress(this.endpoint); } - public Endpoint(Transport transport, String endpoint, MicroserviceInstance instance, Object address) { + public Endpoint(Transport transport, String endpoint, StatefulDiscoveryInstance instance, Object address) { this.transport = transport; this.endpoint = endpoint; this.instance = instance; @@ -56,7 +57,7 @@ public String getEndpoint() { return endpoint; } - public MicroserviceInstance getMicroserviceInstance() { + public DiscoveryInstance getMicroserviceInstance() { return instance; } diff --git a/core/src/main/java/org/apache/servicecomb/core/Invocation.java b/core/src/main/java/org/apache/servicecomb/core/Invocation.java index 1dd42e726d1..067fe6558f4 100644 --- a/core/src/main/java/org/apache/servicecomb/core/Invocation.java +++ b/core/src/main/java/org/apache/servicecomb/core/Invocation.java @@ -32,13 +32,9 @@ import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.event.InvocationBusinessFinishEvent; -import org.apache.servicecomb.core.event.InvocationBusinessMethodFinishEvent; import org.apache.servicecomb.core.event.InvocationBusinessMethodStartEvent; import org.apache.servicecomb.core.event.InvocationEncodeResponseStartEvent; import org.apache.servicecomb.core.event.InvocationFinishEvent; -import org.apache.servicecomb.core.event.InvocationHandlersStartEvent; -import org.apache.servicecomb.core.event.InvocationRunInExecutorFinishEvent; -import org.apache.servicecomb.core.event.InvocationRunInExecutorStartEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; import org.apache.servicecomb.core.event.InvocationStartSendRequestEvent; import org.apache.servicecomb.core.event.InvocationTimeoutCheckEvent; @@ -88,19 +84,15 @@ static Collection loadTraceIdGenerators() { // 同步模式:避免应答在网络线程中处理解码等等业务级逻辑 private Executor responseExecutor; - private boolean sync = true; + private volatile boolean sync = true; private final InvocationStageTrace invocationStageTrace = new InvocationStageTrace(this); private HttpServletRequestEx requestEx; - private boolean finished; + private volatile boolean finished; - // not extend InvocationType - // because isEdge() only affect to apm/metrics output, no need to change so many logic - private boolean edge; - - private long invocationId; + private volatile long invocationId; private TraceIdLogger traceIdLogger; @@ -125,7 +117,7 @@ public Invocation(ReferenceConfig referenceConfig, OperationMeta operationMeta, } public Invocation(Endpoint endpoint, OperationMeta operationMeta, Map swaggerArguments) { - this.invocationType = InvocationType.PRODUCER; + this.invocationType = InvocationType.PROVIDER; this.invocationRuntimeType = operationMeta.buildBaseProviderRuntimeType(); this.endpoint = endpoint; init(operationMeta, swaggerArguments); @@ -261,7 +253,22 @@ public String getOperationName() { return operationMeta.getOperationId(); } + public String getProviderTransportName() { + if (operationMeta.getSwaggerOperation().getExtensions() != null && + operationMeta.getSwaggerOperation().getExtensions().get(CoreConst.TRANSPORT_NAME) != null) { + return (String) operationMeta.getSwaggerOperation().getExtensions().get(CoreConst.TRANSPORT_NAME); + } + if (schemaMeta.getSwagger().getExtensions() != null && + schemaMeta.getSwagger().getExtensions().get(CoreConst.TRANSPORT_NAME) != null) { + return (String) schemaMeta.getSwagger().getExtensions().get(CoreConst.TRANSPORT_NAME); + } + return null; + } + public String getConfigTransportName() { + if (getProviderTransportName() != null) { + return getProviderTransportName(); + } return referenceConfig.getTransport(); } @@ -281,10 +288,6 @@ public MicroserviceMeta getMicroserviceMeta() { return schemaMeta.getMicroserviceMeta(); } - public String getMicroserviceVersionRule() { - return referenceConfig.getVersionRule(); - } - public InvocationRuntimeType getInvocationRuntimeType() { return this.invocationRuntimeType; } @@ -336,55 +339,37 @@ protected void initTraceId(TraceIdGenerator traceIdGenerator) { addContext(traceIdGenerator.getTraceIdKeyName(), traceIdGenerator.generate()); } - public void onStart(long start) { - invocationStageTrace.start(start); + public void onStart() { initTraceId(); EventManager.post(new InvocationStartEvent(this)); } - public void onStart(HttpServletRequestEx requestEx, long start) { + public void onStart(HttpServletRequestEx requestEx) { this.requestEx = requestEx; - onStart(start); - } - - public void onExecuteStart() { - invocationStageTrace.startExecution(); - EventManager.post(new InvocationRunInExecutorStartEvent(this)); - } - - public void onExecuteFinish() { - EventManager.post(new InvocationRunInExecutorFinishEvent(this)); - } - - public void onStartHandlersRequest() { - invocationStageTrace.startHandlersRequest(); - EventManager.post(new InvocationHandlersStartEvent(this)); + onStart(); } public void onStartSendRequest() { - invocationStageTrace.startSend(); EventManager.post(new InvocationStartSendRequestEvent(this)); } - @Override public void onBusinessMethodStart() { - invocationStageTrace.startBusinessMethod(); + invocationStageTrace.startBusinessExecute(); EventManager.post(new InvocationBusinessMethodStartEvent(this)); } - @Override - public void onBusinessMethodFinish() { - EventManager.post(new InvocationBusinessMethodFinishEvent(this)); - } - public void onEncodeResponseStart(Response response) { + invocationStageTrace.startProviderEncodeResponse(); EventManager.post(new InvocationEncodeResponseStartEvent(this, response)); } - @Override + public void onEncodeResponseFinish() { + invocationStageTrace.finishProviderEncodeResponse(); + } + public void onBusinessFinish() { - invocationStageTrace.finishBusiness(); + invocationStageTrace.finishBusinessExecute(); EventManager.post(new InvocationBusinessFinishEvent(this)); } @@ -394,9 +379,9 @@ public void onFinish(Response response) { return; } + finished = true; invocationStageTrace.finish(); EventManager.post(new InvocationFinishEvent(this, response)); - finished = true; } // for retry, reset invocation and try it again @@ -421,19 +406,15 @@ public boolean isConsumer() { } public boolean isProducer() { - return InvocationType.PRODUCER.equals(invocationType); + return InvocationType.PROVIDER.equals(invocationType); } public boolean isEdge() { - return edge; - } - - public void setEdge(boolean edge) { - this.edge = edge; + return InvocationType.EDGE.equals(invocationType); } - public boolean isThirdPartyInvocation() { - return referenceConfig.is3rdPartyService(); + public void setEdge() { + this.invocationType = InvocationType.EDGE; } public long getInvocationId() { @@ -453,7 +434,7 @@ public InvocationStageTrace getInvocationStageTrace() { } public String getTraceId() { - return getContext(Const.TRACE_ID_NAME); + return getContext(CoreConst.TRACE_ID_NAME); } public String getTraceId(String traceIdName) { diff --git a/core/src/main/java/org/apache/servicecomb/core/NonSwaggerInvocation.java b/core/src/main/java/org/apache/servicecomb/core/NonSwaggerInvocation.java index 90f60274147..e7f903e7552 100644 --- a/core/src/main/java/org/apache/servicecomb/core/NonSwaggerInvocation.java +++ b/core/src/main/java/org/apache/servicecomb/core/NonSwaggerInvocation.java @@ -22,22 +22,19 @@ public class NonSwaggerInvocation extends Invocation { private final String microserviceName; - private final String versionRule; - - public NonSwaggerInvocation(String appId, String microserviceName, String versionRule) { + public NonSwaggerInvocation(String appId, String microserviceName) { this.appId = appId; this.microserviceName = microserviceName; - this.versionRule = versionRule; } @Override public String getSchemaId() { - throw new UnsupportedOperationException(); + return "third-schema"; } @Override public String getOperationName() { - throw new UnsupportedOperationException(); + return "third-operation"; } @Override @@ -47,7 +44,7 @@ public String getInvocationQualifiedName() { @Override public String getConfigTransportName() { - return Const.RESTFUL; + return CoreConst.RESTFUL; } @Override @@ -59,9 +56,4 @@ public String getMicroserviceName() { public String getAppId() { return appId; } - - @Override - public String getMicroserviceVersionRule() { - return versionRule; - } } diff --git a/core/src/main/java/org/apache/servicecomb/core/SCBApplicationListener.java b/core/src/main/java/org/apache/servicecomb/core/SCBApplicationListener.java index f8b846fb823..3bd5bd5d8f7 100644 --- a/core/src/main/java/org/apache/servicecomb/core/SCBApplicationListener.java +++ b/core/src/main/java/org/apache/servicecomb/core/SCBApplicationListener.java @@ -21,24 +21,30 @@ import org.apache.servicecomb.core.filter.FilterChainsManager; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; -import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.RegistrationManager; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; +import org.springframework.context.EnvironmentAware; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.core.Ordered; +import org.springframework.core.env.Environment; public class SCBApplicationListener - implements ApplicationListener, Ordered, ApplicationContextAware { + implements ApplicationListener, Ordered, ApplicationContextAware, EnvironmentAware { private Class initEventClass = ContextRefreshedEvent.class; private ApplicationContext applicationContext; + private final SCBEngine scbEngine; + + public SCBApplicationListener(SCBEngine scbEngine) { + this.scbEngine = scbEngine; + } + @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (this.applicationContext == applicationContext) { @@ -48,8 +54,11 @@ public void setApplicationContext(ApplicationContext applicationContext) throws this.applicationContext = applicationContext; BeanUtils.setContext(applicationContext); HttpClients.load(); - RegistrationManager.INSTANCE.init(); - DiscoveryManager.INSTANCE.init(); + } + + @Override + public void setEnvironment(Environment environment) { + scbEngine.init(); } public void setInitEventClass(Class initEventClass) { @@ -69,22 +78,10 @@ public void onApplicationEvent(ApplicationEvent event) { ((AbstractApplicationContext) applicationContext).registerShutdownHook(); } - SCBEngine scbEngine = SCBEngine.getInstance(); - //SCBEngine init first, hence we do not need worry that when other beans need use the - //producer microserviceMeta, the SCBEngine is not inited. -// String serviceName = RegistryUtils.getMicroservice().getServiceName(); -// SCBEngine.getInstance().setProducerMicroserviceMeta(new MicroserviceMeta(serviceName).setConsumer(false)); -// SCBEngine.getInstance().setProducerProviderManager(applicationContext.getBean(ProducerProviderManager.class)); -// SCBEngine.getInstance().setConsumerProviderManager(applicationContext.getBean(ConsumerProviderManager.class)); -// SCBEngine.getInstance().setTransportManager(applicationContext.getBean(TransportManager.class)); - scbEngine.setApplicationContext(applicationContext); scbEngine.setPriorityPropertyManager(applicationContext.getBean(PriorityPropertyManager.class)); scbEngine.setFilterChainsManager(applicationContext.getBean(FilterChainsManager.class)); - scbEngine.getConsumerProviderManager().getConsumerProviderList() - .addAll(applicationContext.getBeansOfType(ConsumerProvider.class).values()); scbEngine.getProducerProviderManager().getProducerProviderList() .addAll(applicationContext.getBeansOfType(ProducerProvider.class).values()); - scbEngine.addBootListeners(applicationContext.getBeansOfType(BootListener.class).values()); scbEngine.run(); } else if (event instanceof ContextClosedEvent) { diff --git a/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java b/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java index b6084622790..c69521698af 100644 --- a/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java +++ b/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java @@ -16,74 +16,58 @@ */ package org.apache.servicecomb.core; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Comparator; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; -import javax.ws.rs.core.Response.Status; - import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.priority.PriorityPropertyManager; import org.apache.servicecomb.core.BootListener.BootEvent; import org.apache.servicecomb.core.BootListener.EventType; import org.apache.servicecomb.core.bootup.BootUpInformationCollector; -import org.apache.servicecomb.core.definition.ConsumerMicroserviceVersionsMeta; -import org.apache.servicecomb.core.definition.CoreMetaUtils; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.MicroserviceVersionsMeta; -import org.apache.servicecomb.core.definition.ServiceRegistryListener; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.core.filter.FilterChainsManager; +import org.apache.servicecomb.core.provider.OpenAPIRegistryManager; import org.apache.servicecomb.core.provider.consumer.ConsumerProviderManager; import org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig; +import org.apache.servicecomb.core.provider.consumer.ReferenceConfigManager; import org.apache.servicecomb.core.provider.producer.ProducerProviderManager; import org.apache.servicecomb.core.transport.TransportManager; -import org.apache.servicecomb.foundation.common.VendorExtensions; -import org.apache.servicecomb.foundation.common.event.EnableExceptionPropagation; import org.apache.servicecomb.foundation.common.event.EventManager; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.registry.api.event.MicroserviceInstanceRegisteredEvent; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstanceStatus; -import org.apache.servicecomb.registry.consumer.MicroserviceVersions; -import org.apache.servicecomb.registry.definition.MicroserviceNameParser; -import org.apache.servicecomb.registry.swagger.SwaggerLoader; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; -import com.netflix.config.DynamicPropertyFactory; + +import jakarta.ws.rs.core.Response.Status; public class SCBEngine { private static final Logger LOGGER = LoggerFactory.getLogger(SCBEngine.class); - static final String CFG_KEY_WAIT_UP_TIMEOUT = "servicecomb.boot.waitUp.timeoutInMilliseconds"; - - static final long DEFAULT_WAIT_UP_TIMEOUT = 10_000; - - static final String CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC = "servicecomb.boot.turnDown.waitInSeconds"; + public static final String CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC = "servicecomb.boot.turnDown.waitInSeconds"; - static final long DEFAULT_TURN_DOWN_STATUS_WAIT_SEC = 0; - - private static final Object initializationLock = new Object(); + public static final long DEFAULT_TURN_DOWN_STATUS_WAIT_SEC = 0; + // TODO: will remove in future. Too many codes need refactor. private static volatile SCBEngine INSTANCE; private ApplicationContext applicationContext; @@ -92,14 +76,13 @@ public class SCBEngine { private ProducerProviderManager producerProviderManager; - private ConsumerProviderManager consumerProviderManager = new ConsumerProviderManager(); + private ConsumerProviderManager consumerProviderManager; private MicroserviceMeta producerMicroserviceMeta; - private TransportManager transportManager = new TransportManager(); + private TransportManager transportManager; - private final List bootListeners = new ArrayList<>( - SPIServiceUtils.getOrLoadSortedService(BootListener.class)); + private List bootListeners; private final AtomicLong invocationStartedCounter = new AtomicLong(); @@ -109,22 +92,25 @@ public class SCBEngine { private final EventBus eventBus; - private ExecutorManager executorManager = new ExecutorManager(); + private ExecutorManager executorManager; private PriorityPropertyManager priorityPropertyManager; - protected List bootUpInformationCollectors = SPIServiceUtils - .getSortedService(BootUpInformationCollector.class); - - private final ServiceRegistryListener serviceRegistryListener; + private List bootUpInformationCollectors; private final SwaggerEnvironment swaggerEnvironment = new SwaggerEnvironment(); - private final VendorExtensions vendorExtensions = new VendorExtensions(); + private OpenAPIRegistryManager openAPIRegistryManager; + + private RegistrationManager registrationManager; + + private DiscoveryManager discoveryManager; - private Thread shutdownHook; + private Environment environment; - protected SCBEngine() { + private ReferenceConfigManager referenceConfigManager; + + public SCBEngine() { eventBus = EventManager.getEventBus(); eventBus.register(this); @@ -132,23 +118,87 @@ protected SCBEngine() { INSTANCE = this; producerProviderManager = new ProducerProviderManager(this); - serviceRegistryListener = new ServiceRegistryListener(this); } - public ApplicationContext getApplicationContext() { - return applicationContext; + public static SCBEngine getInstance() { + if (INSTANCE == null) { + throw new InvocationException(Status.SERVICE_UNAVAILABLE, + new CommonExceptionData("SCBEngine is not initialized yet.")); + } + return INSTANCE; } + @Autowired + public void setReferenceConfigManager(ReferenceConfigManager referenceConfigManager) { + this.referenceConfigManager = referenceConfigManager; + } + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + public Environment getEnvironment() { + return this.environment; + } + + @Autowired + @SuppressWarnings("unused") + public void setBootUpInformationCollectors(List bootUpInformationCollectors) { + this.bootUpInformationCollectors = bootUpInformationCollectors; + } + + @Autowired + @SuppressWarnings("unused") + public void setBootListeners(List listeners) { + this.bootListeners = listeners; + } + + @Autowired public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } - public VendorExtensions getVendorExtensions() { - return vendorExtensions; + @Autowired + public void setRegistrationManager(RegistrationManager registrationManager) { + this.registrationManager = registrationManager; + } + + @Autowired + public void setDiscoveryManager(DiscoveryManager discoveryManager) { + this.discoveryManager = discoveryManager; + } + + @Autowired + public void setOpenAPIRegistryManager(OpenAPIRegistryManager openAPIRegistryManager) { + this.openAPIRegistryManager = openAPIRegistryManager; + } + + @Autowired + public void setConsumerProviderManager(ConsumerProviderManager consumerProviderManager) { + this.consumerProviderManager = consumerProviderManager; + } + + @Autowired + public void setExecutorManager(ExecutorManager executorManager) { + this.executorManager = executorManager; + } + + @Autowired + public void setTransportManager(TransportManager transportManager) { + this.transportManager = transportManager; + } + + public RegistrationManager getRegistrationManager() { + return this.registrationManager; + } + + public ApplicationContext getApplicationContext() { + return applicationContext; } public String getAppId() { - return RegistrationManager.INSTANCE.getAppId(); + return BootStrapProperties.readApplication(environment); } public void setStatus(SCBStatus status) { @@ -159,19 +209,8 @@ public SCBStatus getStatus() { return status; } - public static SCBEngine getInstance() { - if (null == INSTANCE) { - synchronized (initializationLock) { - if (null == INSTANCE) { - new SCBEngine(); - } - } - } - return INSTANCE; - } - - public SwaggerLoader getSwaggerLoader() { - return RegistrationManager.INSTANCE.getSwaggerLoader(); + public OpenAPIRegistryManager getOpenAPIRegistryManager() { + return this.openAPIRegistryManager; } public FilterChainsManager getFilterChainsManager() { @@ -200,10 +239,6 @@ public ExecutorManager getExecutorManager() { return executorManager; } - public void setExecutorManager(ExecutorManager executorManager) { - this.executorManager = executorManager; - } - public ProducerProviderManager getProducerProviderManager() { return producerProviderManager; } @@ -216,32 +251,14 @@ public ConsumerProviderManager getConsumerProviderManager() { return consumerProviderManager; } - public SCBEngine setConsumerProviderManager(ConsumerProviderManager consumerProviderManager) { - this.consumerProviderManager = consumerProviderManager; - return this; - } - public TransportManager getTransportManager() { return transportManager; } - public SCBEngine setTransportManager(TransportManager transportManager) { - this.transportManager = transportManager; - return this; - } - public SwaggerEnvironment getSwaggerEnvironment() { return swaggerEnvironment; } - public Collection getBootListeners() { - return bootListeners; - } - - public void addBootListeners(Collection bootListeners) { - this.bootListeners.addAll(bootListeners); - } - public SCBEngine addProducerMeta(String schemaId, Object instance) { getProducerProviderManager().addProducerMeta(schemaId, instance); return this; @@ -272,40 +289,31 @@ protected void safeTriggerEvent(EventType eventType) { } } - /** - *

As the process of instance registry is asynchronous, the {@code AFTER_REGISTRY} - * event should not be sent immediately. - * When the instance registry succeeds, {@link MicroserviceInstanceRegisteredEvent} will be posted in {@link EventManager}, - * register a subscriber to watch this event and send {@code AFTER_REGISTRY}.

- * - *

This method should be called before registry initialization to avoid that the registry process is too quick - * that the event is not watched by this subscriber.

- * - *

Check if {@code InstanceId} is null to judge whether the instance registry has succeeded.

- */ - private void triggerAfterRegistryEvent() { - eventBus.register(new AfterRegistryEventHanlder(this)); - } - @AllowConcurrentEvents @Subscribe + @SuppressWarnings("unused") public void onInvocationStart(InvocationStartEvent event) { invocationStartedCounter.incrementAndGet(); } @AllowConcurrentEvents @Subscribe + @SuppressWarnings("unused") public void onInvocationFinish(InvocationFinishEvent event) { invocationFinishedCounter.incrementAndGet(); } + public synchronized SCBEngine init() { + this.discoveryManager.init(); + this.registrationManager.init(); + return this; + } + public synchronized SCBEngine run() { if (SCBStatus.DOWN.equals(status)) { try { doRun(); - waitStatusUp(); - } catch (TimeoutException e) { - LOGGER.warn("{}", e.getMessage()); + printServiceInfo(); } catch (Throwable e) { LOGGER.error("Failed to start ServiceComb due to errors and close", e); try { @@ -315,8 +323,6 @@ public synchronized SCBEngine run() { } status = SCBStatus.FAILED; throw new IllegalStateException("ServiceComb init failed.", e); - } finally { - printServiceInfo(); } } @@ -343,8 +349,6 @@ private void printServiceInfo() { private void doRun() throws Exception { status = SCBStatus.STARTING; - bootListeners.sort(Comparator.comparingInt(BootListener::getOrder)); - triggerEvent(EventType.BEFORE_FILTER); filterChainsManager.init(); triggerEvent(EventType.AFTER_FILTER); @@ -364,27 +368,22 @@ private void doRun() throws Exception { triggerEvent(EventType.AFTER_TRANSPORT); triggerEvent(EventType.BEFORE_REGISTRY); + registrationManager.run(); + discoveryManager.run(); + status = SCBStatus.UP; + triggerEvent(EventType.AFTER_REGISTRY); - triggerAfterRegistryEvent(); - - RegistrationManager.INSTANCE.run(); - DiscoveryManager.INSTANCE.run(); - - shutdownHook = new Thread(this::destroyForShutdownHook); - Runtime.getRuntime().addShutdownHook(shutdownHook); + // Keep this message for tests cases work. + LOGGER.warn("ServiceComb is ready."); } private void createProducerMicroserviceMeta() { - String microserviceName = RegistrationManager.INSTANCE.getMicroservice().getServiceName(); - - producerMicroserviceMeta = new MicroserviceMeta(this, microserviceName, false); - producerMicroserviceMeta.setFilterChain(filterChainsManager.findProducerChain(microserviceName)); - producerMicroserviceMeta.setMicroserviceVersionsMeta(new MicroserviceVersionsMeta(this, microserviceName)); - } - - public void destroyForShutdownHook() { - shutdownHook = null; - destroy(); + String microserviceName = BootStrapProperties.readServiceName(environment); + producerMicroserviceMeta = new MicroserviceMeta(this, + BootStrapProperties.readApplication(environment), microserviceName, false); + producerMicroserviceMeta.setProviderFilterChain(filterChainsManager.findProducerChain( + BootStrapProperties.readApplication(environment), microserviceName)); + producerMicroserviceMeta.setMicroserviceVersionsMeta(new MicroserviceVersionsMeta(this)); } /** @@ -401,10 +400,6 @@ public synchronized void destroy() { } private void doDestroy() { - if (shutdownHook != null) { - Runtime.getRuntime().removeShutdownHook(shutdownHook); - } - //Step 0: turn down the status of this instance in service center, // so that the consumers can remove this instance record in advance turnDownInstanceStatus(); @@ -418,10 +413,8 @@ private void doDestroy() { //Step 3: Unregister microservice instance from Service Center and close vertx // Forbidden other consumers find me - RegistrationManager.INSTANCE.destroy(); - DiscoveryManager.INSTANCE.destroy(); - - serviceRegistryListener.destroy(); + registrationManager.destroy(); + discoveryManager.destroy(); //Step 4: wait all invocation finished try { @@ -430,8 +423,7 @@ private void doDestroy() { LOGGER.error("wait all invocation finished interrupted", e); } - //Step 5: destroy config center source - ConfigUtil.destroyConfigCenterConfigurationSource(); + //Step 5: destroy config source // only be null for some test cases if (priorityPropertyManager != null) { priorityPropertyManager.close(); @@ -448,14 +440,17 @@ private void doDestroy() { } private void turnDownInstanceStatus() { - RegistrationManager.INSTANCE.updateMicroserviceInstanceStatus(MicroserviceInstanceStatus.DOWN); + try { + registrationManager.updateMicroserviceInstanceStatus(MicroserviceInstanceStatus.DOWN); + } catch (Throwable e) { + LOGGER.warn("turn down instance status fail: {}", e.getMessage()); + } } private void blockShutDownOperationForConsumerRefresh() { try { - long turnDownWaitSeconds = DynamicPropertyFactory.getInstance() - .getLongProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC) - .get(); + long turnDownWaitSeconds = environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); if (turnDownWaitSeconds <= 0) { return; } @@ -486,51 +481,19 @@ public void ensureStatusUp() { if (!SCBStatus.UP.equals(currentStatus)) { String message = "The request is rejected. Cannot process the request due to STATUS = " + currentStatus; - LOGGER.warn(message); throw new InvocationException(Status.SERVICE_UNAVAILABLE, new CommonExceptionData(message)); } } - /** - * for normal consumers - * @param microserviceName shortName, or appId:shortName when invoke cross app - * @return - */ - public MicroserviceReferenceConfig createMicroserviceReferenceConfig(String microserviceName) { - return createMicroserviceReferenceConfig(microserviceName, null); + public CompletableFuture getOrCreateReferenceConfigAsync( + String microserviceName) { + return referenceConfigManager.getOrCreateReferenceConfigAsync(this, microserviceName); } - /** - * for edge, versionRule maybe controlled by url rule - * @param microserviceName hortName, or appId:shortName when invoke cross app - * @param versionRule if is empty, then use configuration value - * @return - */ - public CompletableFuture createMicroserviceReferenceConfigAsync(String microserviceName, - String versionRule) { - return DiscoveryManager.INSTANCE - .getOrCreateMicroserviceVersionsAsync(parseAppId(microserviceName), microserviceName) - .thenApply(versions -> { - ConsumerMicroserviceVersionsMeta microserviceVersionsMeta = CoreMetaUtils - .getMicroserviceVersionsMeta(versions); - return new MicroserviceReferenceConfig(microserviceVersionsMeta, versionRule); - }); - } - - /** - * for edge, versionRule maybe controlled by url rule - * @param microserviceName hortName, or appId:shortName when invoke cross app - * @param versionRule if is empty, then use configuration value - * @return - */ - public MicroserviceReferenceConfig createMicroserviceReferenceConfig(String microserviceName, String versionRule) { + public MicroserviceReferenceConfig getOrCreateReferenceConfig( + String microserviceName) { ensureStatusUp(); - MicroserviceVersions microserviceVersions = DiscoveryManager.INSTANCE - .getOrCreateMicroserviceVersions(parseAppId(microserviceName), microserviceName); - ConsumerMicroserviceVersionsMeta microserviceVersionsMeta = CoreMetaUtils - .getMicroserviceVersionsMeta(microserviceVersions); - - return new MicroserviceReferenceConfig(microserviceVersionsMeta, versionRule); + return referenceConfigManager.getOrCreateReferenceConfig(this, microserviceName); } public MicroserviceMeta getProducerMicroserviceMeta() { @@ -541,75 +504,16 @@ public void setProducerMicroserviceMeta(MicroserviceMeta producerMicroserviceMet this.producerMicroserviceMeta = producerMicroserviceMeta; } - /** - * better to subscribe EventType.AFTER_REGISTRY by BootListener
- * but in some simple scenes, just block and wait is enough. - */ - public void waitStatusUp() throws InterruptedException, TimeoutException { - long msWait = DynamicPropertyFactory.getInstance().getLongProperty(CFG_KEY_WAIT_UP_TIMEOUT, DEFAULT_WAIT_UP_TIMEOUT) - .get(); - waitStatusUp(msWait); - } - - /** - * better to subscribe EventType.AFTER_REGISTRY by BootListener
- * but in some simple scenes, just block and wait is enough. - */ - public void waitStatusUp(long msWait) throws InterruptedException, TimeoutException { - if (msWait <= 0) { - LOGGER.info("Give up waiting for status up, wait timeout milliseconds={}.", msWait); - return; - } - - LOGGER.info("Waiting for status up. timeout: {}ms", msWait); - long start = System.currentTimeMillis(); - for (; ; ) { - SCBStatus currentStatus = getStatus(); - switch (currentStatus) { - case DOWN: - case FAILED: - throw new IllegalStateException("Failed to wait status up, real status: " + currentStatus); - case UP: - LOGGER.info("Status already changed to up."); - return; - default: - break; - } - - TimeUnit.MILLISECONDS.sleep(100); - if (System.currentTimeMillis() - start > msWait) { - throw new TimeoutException( - String.format("Timeout to wait status up, timeout: %dms, last status: %s", msWait, currentStatus)); - } - } - } - - public String parseAppId(String microserviceName) { - return parseMicroserviceName(microserviceName).getAppId(); - } - public MicroserviceNameParser parseMicroserviceName(String microserviceName) { - return new MicroserviceNameParser(getAppId(), microserviceName); - } - - public static class AfterRegistryEventHanlder { - private final SCBEngine engine; + public static class CreateMicroserviceMetaEvent { + private final MicroserviceMeta microserviceMeta; - public AfterRegistryEventHanlder(SCBEngine engine) { - this.engine = engine; + public CreateMicroserviceMetaEvent(MicroserviceMeta microserviceMeta) { + this.microserviceMeta = microserviceMeta; } - @Subscribe - @EnableExceptionPropagation - public void afterRegistryInstance(MicroserviceInstanceRegisteredEvent event) { - if (event.isRegistrationManager()) { - LOGGER.info("instance registry succeeds for the first time, will send AFTER_REGISTRY event."); - engine.setStatus(SCBStatus.UP); - engine.triggerEvent(EventType.AFTER_REGISTRY); - EventManager.unregister(this); - // keep this message to be WARN, used to detect service ready. - LOGGER.warn("ServiceComb is ready."); - } + public MicroserviceMeta getMicroserviceMeta() { + return this.microserviceMeta; } } } diff --git a/core/src/main/java/org/apache/servicecomb/core/ServiceCombCoreConfiguration.java b/core/src/main/java/org/apache/servicecomb/core/ServiceCombCoreConfiguration.java index a512c45cdfd..8abaceef9b5 100644 --- a/core/src/main/java/org/apache/servicecomb/core/ServiceCombCoreConfiguration.java +++ b/core/src/main/java/org/apache/servicecomb/core/ServiceCombCoreConfiguration.java @@ -16,21 +16,98 @@ */ package org.apache.servicecomb.core; +import org.apache.servicecomb.core.bootup.ConfigurationProblemsCollector; +import org.apache.servicecomb.core.bootup.FilterChainCollector; +import org.apache.servicecomb.core.bootup.ServiceInformationCollector; +import org.apache.servicecomb.core.executor.ExecutorManager; +import org.apache.servicecomb.core.executor.GroupExecutor; +import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; +import org.apache.servicecomb.core.provider.OpenAPIRegistryManager; +import org.apache.servicecomb.core.provider.RegistryOpenAPIRegistry; +import org.apache.servicecomb.core.provider.consumer.ConsumerProviderManager; +import org.apache.servicecomb.core.provider.consumer.ReferenceConfigManager; +import org.apache.servicecomb.core.provider.producer.ProducerBootListener; +import org.apache.servicecomb.core.transport.TransportManager; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; @Configuration +@SuppressWarnings("unused") public class ServiceCombCoreConfiguration { @Bean - public ConfigurationSpringInitializer configurationSpringInitializer() { - return new ConfigurationSpringInitializer(); + public SCBApplicationListener scbApplicationListener(SCBEngine scbEngine) { + SCBApplicationListener scbApplicationListener = new SCBApplicationListener(scbEngine); + scbApplicationListener.setInitEventClass(ApplicationReadyEvent.class); + return scbApplicationListener; } @Bean - public SCBApplicationListener scbApplicationListener() { - SCBApplicationListener scbApplicationListener = new SCBApplicationListener(); - scbApplicationListener.setInitEventClass(ApplicationReadyEvent.class); - return scbApplicationListener; + public SCBEngine scbEngine() { + return new SCBEngine(); + } + + @Bean + public ConsumerProviderManager scbConsumerProviderManager(Environment environment, + OpenAPIRegistryManager openAPIRegistryManager) { + return new ConsumerProviderManager(environment, openAPIRegistryManager); + } + + @Bean + public ReferenceConfigManager scbReferenceConfigManager() { + return new ReferenceConfigManager(); + } + + @Bean + public OpenAPIRegistryManager scbOpenAPIRegistryManager() { + return new OpenAPIRegistryManager(); + } + + @Bean + public LocalOpenAPIRegistry scbLocalOpenAPIRegistry(Environment environment) { + return new LocalOpenAPIRegistry(environment); + } + + @Bean + public RegistryOpenAPIRegistry scbRegistryOpenAPIRegistry() { + return new RegistryOpenAPIRegistry(); + } + + @Bean + public ProducerBootListener scbProducerBootListener() { + return new ProducerBootListener(); + } + + @Bean + public FilterChainCollector scbFilterChainCollector() { + return new FilterChainCollector(); + } + + @Bean + public ConfigurationProblemsCollector scbConfigurationProblemsCollector() { + return new ConfigurationProblemsCollector(); + } + + @Bean + public ServiceInformationCollector scbServiceInformationCollector() { + return new ServiceInformationCollector(); + } + + @Bean + public ExecutorManager scbExecutorManager() { + return new ExecutorManager(); + } + + @Bean(value = {"cse.executor.groupThreadPool", "cse.executor.default", "servicecomb.executor.groupThreadPool"}) + public GroupExecutor scbGroupExecutor(Environment environment) { + GroupExecutor groupExecutor = new GroupExecutor(environment); + groupExecutor.init(); + return groupExecutor; + } + + @Bean + public TransportManager scbTransportManager() { + return new TransportManager(); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/Transport.java b/core/src/main/java/org/apache/servicecomb/core/Transport.java index ed9ade43e88..1c40303fce8 100644 --- a/core/src/main/java/org/apache/servicecomb/core/Transport.java +++ b/core/src/main/java/org/apache/servicecomb/core/Transport.java @@ -17,9 +17,8 @@ package org.apache.servicecomb.core; -import org.apache.servicecomb.swagger.invocation.AsyncResponse; +import org.springframework.core.env.Environment; -// TODO:感觉要拆成显式的client、server才好些 public interface Transport { String getName(); @@ -33,6 +32,8 @@ default boolean canInit() { boolean init() throws Exception; + void setEnvironment(Environment environment); + /* * endpoint的格式为 URI,比如rest://192.168.1.1:8080 */ @@ -47,6 +48,4 @@ default boolean canInit() { * 用于上报到服务中心,要求是其他节点可访问的地址 */ Endpoint getPublishEndpoint() throws Exception; - - void send(Invocation invocation, AsyncResponse asyncResp) throws Exception; } diff --git a/core/src/main/java/org/apache/servicecomb/core/annotation/Transport.java b/core/src/main/java/org/apache/servicecomb/core/annotation/Transport.java new file mode 100644 index 00000000000..c81055c7b46 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/annotation/Transport.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.servicecomb.core.annotation; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import org.springframework.stereotype.Component; + +@Inherited +@Documented +@Retention(RUNTIME) +@Target({TYPE, METHOD, ANNOTATION_TYPE}) +@Component +public @interface Transport { + /** + * Transport name. e.g. rest, highway. + */ + String name(); +} diff --git a/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBBootstrap.java b/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBBootstrap.java index 1967e5609b0..21040a3410f 100644 --- a/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBBootstrap.java +++ b/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBBootstrap.java @@ -16,14 +16,41 @@ */ package org.apache.servicecomb.core.bootstrap; +import java.util.Collections; +import java.util.List; + import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.executor.ExecutorManager; +import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; +import org.apache.servicecomb.core.provider.OpenAPIRegistryManager; +import org.apache.servicecomb.core.provider.consumer.ConsumerProviderManager; +import org.apache.servicecomb.core.transport.TransportManager; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.RegistrationManager; +import org.apache.servicecomb.registry.discovery.TelnetInstancePing; +import org.springframework.core.env.Environment; public class SCBBootstrap { - public static SCBEngine createSCBEngineForTest() { - RegistrationManager.INSTANCE.init(); - DiscoveryManager.INSTANCE.init(); - return new SCBEngineForTest(); + public static SCBEngine createSCBEngineForTest(Environment environment) { + LegacyPropertyFactory.setEnvironment(environment); + RegistrationManager registrationManager = new RegistrationManager(Collections.emptyList()); + DiscoveryManager discoveryManager = new DiscoveryManager(Collections.emptyList(), + List.of(new TelnetInstancePing())); + registrationManager.init(); + discoveryManager.init(); + SCBEngine result = new SCBEngineForTest(environment); + result.setDiscoveryManager(discoveryManager); + result.setRegistrationManager(registrationManager); + result.setBootListeners(Collections.emptyList()); + result.setBootUpInformationCollectors(Collections.emptyList()); + result.setExecutorManager(new ExecutorManager()); + result.setTransportManager(new TransportManager()); + result.setEnvironment(environment); + OpenAPIRegistryManager openAPIRegistryManager = new OpenAPIRegistryManager(); + openAPIRegistryManager.setOpenAPIRegistries(List.of(new LocalOpenAPIRegistry(environment))); + result.setConsumerProviderManager(new ConsumerProviderManager(environment, openAPIRegistryManager)); + result.setOpenAPIRegistryManager(openAPIRegistryManager); + return result; } } diff --git a/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBEngineForTest.java b/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBEngineForTest.java index 71b5af2b3ba..46ccd954155 100644 --- a/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBEngineForTest.java +++ b/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBEngineForTest.java @@ -17,39 +17,35 @@ package org.apache.servicecomb.core.bootstrap; -import static org.apache.servicecomb.core.executor.ExecutorManager.EXECUTOR_GROUP_THREADPOOL; - -import java.util.Arrays; import java.util.List; import org.apache.servicecomb.config.priority.ConfigObjectFactory; import org.apache.servicecomb.config.priority.PriorityPropertyFactory; import org.apache.servicecomb.config.priority.PriorityPropertyManager; import org.apache.servicecomb.core.SCBEngine; -import org.apache.servicecomb.core.executor.GroupExecutor; -import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterChainsManager; import org.apache.servicecomb.core.filter.impl.EmptyFilter; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.event.SimpleEventBus; import org.apache.servicecomb.foundation.common.utils.ReflectUtils; +import org.springframework.core.env.Environment; /** * not depend on remote service registry and spring context */ public class SCBEngineForTest extends SCBEngine { - public SCBEngineForTest() { - getExecutorManager().registerExecutor(EXECUTOR_GROUP_THREADPOOL, new GroupExecutor().init()); - - List filters = Arrays.asList( - new EmptyFilter() - ); + public SCBEngineForTest(Environment environment) { + EmptyFilter emptyFilter = new EmptyFilter(); + emptyFilter.setEnvironment(environment); setFilterChainsManager(new FilterChainsManager() - .addFilters(filters)); + .setProviderFilters(List.of(emptyFilter)) + .setConsumerFilters(List.of(emptyFilter)) + .setEdgeFilters(List.of(emptyFilter))); - PriorityPropertyFactory propertyFactory = new PriorityPropertyFactory(); + PriorityPropertyFactory propertyFactory = new PriorityPropertyFactory(environment); ConfigObjectFactory configObjectFactory = new ConfigObjectFactory(propertyFactory); setPriorityPropertyManager(new PriorityPropertyManager(configObjectFactory)); + setEnvironment(environment); } @Override diff --git a/core/src/main/java/org/apache/servicecomb/core/bootup/BootUpInformationCollector.java b/core/src/main/java/org/apache/servicecomb/core/bootup/BootUpInformationCollector.java index 62a9ac5ce2b..4ebae5180f4 100644 --- a/core/src/main/java/org/apache/servicecomb/core/bootup/BootUpInformationCollector.java +++ b/core/src/main/java/org/apache/servicecomb/core/bootup/BootUpInformationCollector.java @@ -18,13 +18,12 @@ package org.apache.servicecomb.core.bootup; import org.apache.servicecomb.core.SCBEngine; +import org.springframework.core.Ordered; -public interface BootUpInformationCollector { +public interface BootUpInformationCollector extends Ordered { default String collect(SCBEngine engine) { return collect(); } String collect(); - - int getOrder(); } diff --git a/core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsAlarmEvent.java b/core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsAlarmEvent.java new file mode 100644 index 00000000000..631cc361235 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsAlarmEvent.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.servicecomb.core.bootup; + +import org.apache.servicecomb.foundation.common.event.AlarmEvent; + +public class ConfigurationProblemsAlarmEvent extends AlarmEvent { + private final String problems; + + public ConfigurationProblemsAlarmEvent(Type type, String problems) { + super(type); + this.problems = problems; + } + + public String getProblems() { + return problems; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsCollector.java b/core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsCollector.java new file mode 100644 index 00000000000..415516f6fea --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsCollector.java @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.bootup; + +import java.util.Arrays; +import java.util.Set; +import java.util.regex.Pattern; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.foundation.common.event.AlarmEvent.Type; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.springframework.core.env.Environment; + +/** + * Detect deprecated and wrong usages of configurations + * and print warning messages + * and sending ConfigurationProblemsAlarmEvent. + */ +public class ConfigurationProblemsCollector implements BootUpInformationCollector { + private static final String SERVICE_NAME_PATTERN_STRING = "([A-Za-z])|([A-Za-z][A-Za-z0-9_\\-.]*[A-Za-z0-9])"; + + private static final Pattern SERVICE_NAME_PATTERN = Pattern.compile(SERVICE_NAME_PATTERN_STRING); + + @Override + public String collect(SCBEngine engine) { + StringBuilder result = new StringBuilder(); + collectCsePrefix(engine.getEnvironment(), result); + collectServiceDefinition(engine.getEnvironment(), result); + collectServiceDefinitionValidation(engine.getEnvironment(), result); + collectTimeoutConfiguration(engine.getEnvironment(), result); + collectIsolationConfiguration(engine.getEnvironment(), result); + if (result.isEmpty()) { + return null; + } + String warnings = "[WARN]Configurations warnings:\n" + result; + EventManager.post(new ConfigurationProblemsAlarmEvent(Type.OPEN, warnings)); + return warnings; + } + + private void collectServiceDefinitionValidation(Environment environment, StringBuilder result) { + String application = BootStrapProperties.readApplication(environment); + if (!SERVICE_NAME_PATTERN.matcher(application).matches()) { + result.append("application does not match pattern ").append(SERVICE_NAME_PATTERN_STRING).append("."); + } + String serviceName = BootStrapProperties.readServiceName(environment); + if (!SERVICE_NAME_PATTERN.matcher(serviceName).matches()) { + result.append("service name does not match pattern ").append(SERVICE_NAME_PATTERN_STRING).append("."); + } + } + + private void collectIsolationConfiguration(Environment environment, StringBuilder result) { + int percentage = environment.getProperty( + "servicecomb.loadbalance.isolation.errorThresholdPercentage", int.class, -1); + int continuous = environment.getProperty( + "servicecomb.loadbalance.isolation.continuousFailureThreshold", int.class, -1); + if (percentage == -1 && continuous == -1) { + return; + } + result.append("Configuration `servicecomb.loadbalance.isolation.*` is removed, use governance instead. " + + "See https://servicecomb.apache.org/references/java-chassis/" + + "zh_CN/references-handlers/governance-best-practise.html"); + } + + private void collectTimeoutConfiguration(Environment environment, StringBuilder result) { + int keepAliveTimeoutInSeconds = environment.getProperty( + "servicecomb.rest.client.connection.keepAliveTimeoutInSeconds", int.class, 60); + int idleTimeoutInSeconds = environment.getProperty( + "servicecomb.rest.client.connection.idleTimeoutInSeconds", int.class, 150); + if (keepAliveTimeoutInSeconds >= idleTimeoutInSeconds) { + result.append("Configuration `servicecomb.rest.client.connection.keepAliveTimeoutInSeconds` is longer than " + + "servicecomb.rest.client.connection.idleTimeoutInSeconds."); + result.append("[").append(keepAliveTimeoutInSeconds).append(",").append(idleTimeoutInSeconds).append("]\n"); + } + keepAliveTimeoutInSeconds = environment.getProperty( + "servicecomb.rest.client.http2.connection.keepAliveTimeoutInSeconds", int.class, 60); + idleTimeoutInSeconds = environment.getProperty( + "servicecomb.rest.client.http2.connection.idleTimeoutInSeconds", int.class, 150); + if (keepAliveTimeoutInSeconds >= idleTimeoutInSeconds) { + result.append("Configuration `servicecomb.rest.client.http2.connection.keepAliveTimeoutInSeconds` is longer than " + + "servicecomb.rest.client.http2.connection.idleTimeoutInSeconds."); + result.append("[").append(keepAliveTimeoutInSeconds).append(",").append(idleTimeoutInSeconds).append("]\n"); + } + } + + private void collectServiceDefinition(Environment environment, StringBuilder result) { + if (environment.getProperty("APPLICATION_ID") != null) { + result.append("Configurations `APPLICATION_ID` is deprecated, " + + "use `servicecomb.service.application` instead.\n"); + } + Set names = ConfigUtil.propertiesWithPrefix(environment, "service_description."); + if (!names.isEmpty()) { + result.append("Configurations with prefix `service_description` is deprecated, " + + "use `servicecomb.service` instead. Find keys "); + result.append(Arrays.toString(names.toArray())); + result.append("\n"); + } + } + + private void collectCsePrefix(Environment environment, StringBuilder result) { + Set names = ConfigUtil.propertiesWithPrefix(environment, "cse."); + if (!names.isEmpty()) { + result.append("Configurations with prefix `cse` is deprecated, use `servicecomb` instead. Find keys "); + result.append(Arrays.toString(names.toArray())); + result.append("\n"); + } + } + + @Override + public String collect() { + return null; + } + + @Override + public int getOrder() { + return 1000; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/bootup/ServiceInformationCollector.java b/core/src/main/java/org/apache/servicecomb/core/bootup/ServiceInformationCollector.java index b94b947993d..fb13033ec99 100644 --- a/core/src/main/java/org/apache/servicecomb/core/bootup/ServiceInformationCollector.java +++ b/core/src/main/java/org/apache/servicecomb/core/bootup/ServiceInformationCollector.java @@ -17,13 +17,31 @@ package org.apache.servicecomb.core.bootup; +import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.RegistrationManager; +import org.springframework.beans.factory.annotation.Autowired; public class ServiceInformationCollector implements BootUpInformationCollector { + private RegistrationManager registrationManager; + + private DiscoveryManager discoveryManager; + + public ServiceInformationCollector() { + } + + @Autowired + public void setRegistrationManager(RegistrationManager registrationManager) { + this.registrationManager = registrationManager; + } + + @Autowired + public void setDiscoveryManager(DiscoveryManager discoveryManager) { + this.discoveryManager = discoveryManager; + } @Override public String collect() { - return RegistrationManager.INSTANCE.info(); + return registrationManager.info() + "\n" + discoveryManager.info(); } @Override diff --git a/core/src/main/java/org/apache/servicecomb/core/definition/ConsumerMicroserviceVersionsMeta.java b/core/src/main/java/org/apache/servicecomb/core/definition/ConsumerMicroserviceVersionsMeta.java index f2f6aa33e79..78f24e67f78 100644 --- a/core/src/main/java/org/apache/servicecomb/core/definition/ConsumerMicroserviceVersionsMeta.java +++ b/core/src/main/java/org/apache/servicecomb/core/definition/ConsumerMicroserviceVersionsMeta.java @@ -17,18 +17,9 @@ package org.apache.servicecomb.core.definition; import org.apache.servicecomb.core.SCBEngine; -import org.apache.servicecomb.registry.consumer.MicroserviceVersions; public class ConsumerMicroserviceVersionsMeta extends MicroserviceVersionsMeta { - private final MicroserviceVersions microserviceVersions; - - public ConsumerMicroserviceVersionsMeta(SCBEngine scbEngine, MicroserviceVersions microserviceVersions) { - super(scbEngine, microserviceVersions.getMicroserviceName()); - - this.microserviceVersions = microserviceVersions; - } - - public MicroserviceVersions getMicroserviceVersions() { - return microserviceVersions; + public ConsumerMicroserviceVersionsMeta(SCBEngine scbEngine) { + super(scbEngine); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/definition/CoreMetaUtils.java b/core/src/main/java/org/apache/servicecomb/core/definition/CoreMetaUtils.java index df4bd490b53..9b164223af7 100644 --- a/core/src/main/java/org/apache/servicecomb/core/definition/CoreMetaUtils.java +++ b/core/src/main/java/org/apache/servicecomb/core/definition/CoreMetaUtils.java @@ -16,48 +16,10 @@ */ package org.apache.servicecomb.core.definition; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.registry.consumer.MicroserviceVersion; -import org.apache.servicecomb.registry.consumer.MicroserviceVersions; -import org.apache.servicecomb.swagger.engine.SwaggerProducer; - public final class CoreMetaUtils { - public static final String CORE_MICROSERVICE_VERSIONS_META = "scb_microservice_versions_meta"; - - public static final String CORE_MICROSERVICE_META = "scb_microservice_meta"; - - public static final String CORE_MICROSERVICE_VERSION = "scb_microservice_version"; public static final String SWAGGER_PRODUCER = "scb_swagger-producer"; private CoreMetaUtils() { } - - public static T getMicroserviceVersionsMeta( - MicroserviceVersions microserviceVersions) { - return microserviceVersions.getVendorExtensions().get(CORE_MICROSERVICE_VERSIONS_META); - } - - // only for consumer flow - public static MicroserviceVersions getMicroserviceVersions(Invocation invocation) { - return getMicroserviceVersions(invocation.getMicroserviceMeta()); - } - - // only for consumer flow - public static MicroserviceVersions getMicroserviceVersions(MicroserviceMeta microserviceMeta) { - return getMicroserviceVersion(microserviceMeta).getMicroserviceVersions(); - } - - // only for consumer flow - public static MicroserviceVersion getMicroserviceVersion(MicroserviceMeta microserviceMeta) { - return microserviceMeta.getExtData(CORE_MICROSERVICE_VERSION); - } - - public static MicroserviceMeta getMicroserviceMeta(MicroserviceVersion microserviceVersion) { - return microserviceVersion.getVendorExtensions().get(CORE_MICROSERVICE_META); - } - - public static SwaggerProducer getSwaggerProducer(SchemaMeta schemaMeta) { - return schemaMeta.getExtData(SWAGGER_PRODUCER); - } } diff --git a/core/src/main/java/org/apache/servicecomb/core/definition/InvocationRuntimeType.java b/core/src/main/java/org/apache/servicecomb/core/definition/InvocationRuntimeType.java index 84351047b94..356201b70d7 100644 --- a/core/src/main/java/org/apache/servicecomb/core/definition/InvocationRuntimeType.java +++ b/core/src/main/java/org/apache/servicecomb/core/definition/InvocationRuntimeType.java @@ -19,7 +19,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; -import javax.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.response.ResponsesMeta; diff --git a/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceConfig.java b/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceConfig.java deleted file mode 100644 index 68f58b6ab48..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceConfig.java +++ /dev/null @@ -1,39 +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.servicecomb.core.definition; - -import org.apache.servicecomb.config.inject.InjectProperties; -import org.apache.servicecomb.config.inject.InjectProperty; -import org.apache.servicecomb.registry.version.VersionRuleUtils; - -@InjectProperties(prefix = "servicecomb") -public class MicroserviceConfig { - @InjectProperty(keys = { - "references.version-rule.${service}", - "references.${service}.version-rule", // Deprecated - "references.version-rule" - }, defaultValue = "0.0.0.0+") - private String versionRule; - - public String getVersionRule() { - return versionRule; - } - - public void setVersionRule(String versionRule) { - this.versionRule = VersionRuleUtils.getOrCreate(versionRule).getVersionRule(); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceMeta.java b/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceMeta.java index f953b7393c4..9476a34229b 100644 --- a/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceMeta.java +++ b/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceMeta.java @@ -16,25 +16,16 @@ */ package org.apache.servicecomb.core.definition; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.common.VendorExtensions; -import org.apache.servicecomb.registry.definition.MicroserviceNameParser; -import org.apache.servicecomb.swagger.SwaggerUtils; -import io.swagger.models.Swagger; +import io.swagger.v3.oas.models.OpenAPI; -/** - * should named MicroserviceVersionMeta
- * but for compatible reason, keep the old name - */ public class MicroserviceMeta { private final SCBEngine scbEngine; @@ -42,45 +33,32 @@ public class MicroserviceMeta { private final String appId; - // always not include appId - private final String shortName; - - // inside app: equals to shortName - // cross app: appId:shortName private final String microserviceName; // key is schemaId, this is all schemas private final Map schemaMetas = new HashMap<>(); - // key is schema interface - // only when list have only one element, then allow query by interface - // otherwise must query by schemaId - // - // value is synchronizedList, only for low frequency query - private final Map, List> intfSchemaMetas = new HashMap<>(); - // key is OperationMeta.getMicroserviceQualifiedName() private final Map operationMetas = new HashMap<>(); + // Used to indicate configuration items type. EDGE & CONSUMER chain are all consumer. private final boolean consumer; - private FilterNode filterChain = FilterNode.EMPTY; + private FilterNode consumerFilterChain = FilterNode.EMPTY; + + private FilterNode providerFilterChain = FilterNode.EMPTY; + + private FilterNode edgeFilterChain = FilterNode.EMPTY; private final VendorExtensions vendorExtensions = new VendorExtensions(); - public MicroserviceMeta(SCBEngine scbEngine, String microserviceName, boolean consumer) { + public MicroserviceMeta(SCBEngine scbEngine, String application, String serviceName, boolean consumer) { this.scbEngine = scbEngine; - MicroserviceNameParser parser = scbEngine.parseMicroserviceName(microserviceName); - this.appId = parser.getAppId(); - this.shortName = parser.getShortName(); - this.microserviceName = parser.getMicroserviceName(); + this.appId = application; + this.microserviceName = serviceName; this.consumer = consumer; } - public MicroserviceConfig getMicroserviceConfig() { - return microserviceVersionsMeta.getMicroserviceConfig(); - } - public MicroserviceVersionsMeta getMicroserviceVersionsMeta() { return microserviceVersionsMeta; } @@ -105,11 +83,7 @@ public String getAppId() { return appId; } - public String getShortName() { - return shortName; - } - - public SchemaMeta registerSchemaMeta(String schemaId, Swagger swagger) { + public SchemaMeta registerSchemaMeta(String schemaId, OpenAPI swagger) { SchemaMeta schemaMeta = new SchemaMeta(this, schemaId, swagger); if (schemaMetas.putIfAbsent(schemaMeta.getSchemaId(), schemaMeta) != null) { @@ -118,14 +92,7 @@ public SchemaMeta registerSchemaMeta(String schemaId, Swagger swagger) { appId, microserviceName, schemaMeta.getSchemaId())); } - Class intf = SwaggerUtils.getInterface(schemaMeta.getSwagger()); - if (intf != null) { - intfSchemaMetas - .computeIfAbsent(intf, k -> Collections.synchronizedList(new ArrayList<>())) - .add(schemaMeta); - } - - schemaMeta.getOperations().values().stream() + schemaMeta.getOperations().values() .forEach(operationMeta -> operationMetas.put(operationMeta.getMicroserviceQualifiedName(), operationMeta)); return schemaMeta; @@ -154,23 +121,6 @@ public SchemaMeta findSchemaMeta(String schemaId) { return schemaMetas.get(schemaId); } - public SchemaMeta findSchemaMeta(Class schemaIntf) { - List schemaList = intfSchemaMetas.get(schemaIntf); - if (schemaList == null) { - return null; - } - - if (schemaList.size() > 1) { - throw new IllegalStateException(String.format( - "failed to find SchemaMeta by interface cause there are multiple SchemaMeta relate to the interface, " - + "please use schemaId to choose a SchemaMeta, " - + "appId=%s, microserviceName=%s, interface=%s.", - appId, microserviceName, schemaIntf.getName())); - } - - return schemaList.get(0); - } - public Map getSchemaMetas() { return schemaMetas; } @@ -183,11 +133,27 @@ public T getExtData(String key) { return vendorExtensions.get(key); } - public FilterNode getFilterChain() { - return filterChain; + public FilterNode getConsumerFilterChain() { + return consumerFilterChain; + } + + public void setConsumerFilterChain(FilterNode consumerFilterChain) { + this.consumerFilterChain = consumerFilterChain; + } + + public FilterNode getProviderFilterChain() { + return providerFilterChain; + } + + public void setProviderFilterChain(FilterNode providerFilterChain) { + this.providerFilterChain = providerFilterChain; + } + + public FilterNode getEdgeFilterChain() { + return edgeFilterChain; } - public void setFilterChain(FilterNode filterChain) { - this.filterChain = filterChain; + public void setEdgeFilterChain(FilterNode edgeFilterChain) { + this.edgeFilterChain = edgeFilterChain; } } diff --git a/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceVersionsMeta.java b/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceVersionsMeta.java index 05f336200b8..9e98bc2db51 100644 --- a/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceVersionsMeta.java +++ b/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceVersionsMeta.java @@ -24,32 +24,11 @@ public class MicroserviceVersionsMeta { protected final SCBEngine scbEngine; - protected final String microserviceName; - // key is operationMeta.getMicroserviceQualifiedName() private final Map configs = new ConcurrentHashMapEx<>(); - protected volatile MicroserviceConfig microserviceConfig; - - public MicroserviceVersionsMeta(SCBEngine scbEngine, String microserviceName) { + public MicroserviceVersionsMeta(SCBEngine scbEngine) { this.scbEngine = scbEngine; - this.microserviceName = microserviceName; - } - - // should not create in constructor - // when invoke not exist microservice, will create DynamicProperty(com.netflix.config.DynamicProperty.getInstance) - // edge scene, if attacker request to forward request to not exist microservice, will cause OOM - public MicroserviceConfig getMicroserviceConfig() { - if (microserviceConfig == null) { - synchronized (this) { - if (microserviceConfig == null) { - this.microserviceConfig = scbEngine.getPriorityPropertyManager() - .createConfigObject(MicroserviceConfig.class, "service", microserviceName); - } - } - } - - return microserviceConfig; } public OperationConfig getOrCreateOperationConfig(OperationMeta operationMeta) { diff --git a/core/src/main/java/org/apache/servicecomb/core/definition/OperationConfig.java b/core/src/main/java/org/apache/servicecomb/core/definition/OperationConfig.java index 972f7a6ae80..bc289521a31 100644 --- a/core/src/main/java/org/apache/servicecomb/core/definition/OperationConfig.java +++ b/core/src/main/java/org/apache/servicecomb/core/definition/OperationConfig.java @@ -24,7 +24,7 @@ import org.apache.servicecomb.config.inject.InjectProperties; import org.apache.servicecomb.config.inject.InjectProperty; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; @InjectProperties(prefix = "servicecomb") public class OperationConfig { @@ -75,8 +75,8 @@ public class OperationConfig { /** * whether to remove certain headers from the 3rd party invocations */ - @InjectProperty(keys = {"request.clientRequestHeaderFilterEnabled${consumer-op-priority}"}, defaultValue = "true") - private boolean clientRequestHeaderFilterEnabled = true; + @InjectProperty(keys = {"request.clientRequestHeaderFilterEnabled${consumer-op-priority}"}, defaultValue = "false") + private boolean clientRequestHeaderFilterEnabled = false; /** * producer wait in thread pool timeout @@ -168,7 +168,7 @@ public long getMsHighwayRequestWaitInPoolTimeout() { public void setMsHighwayRequestWaitInPoolTimeout(long msHighwayRequestWaitInPoolTimeout) { this.msHighwayRequestWaitInPoolTimeout = msHighwayRequestWaitInPoolTimeout; this.nanoHighwayRequestWaitInPoolTimeout = TimeUnit.MILLISECONDS.toNanos(msHighwayRequestWaitInPoolTimeout); - registerRequestWaitInPoolTimeout(Const.HIGHWAY, msHighwayRequestWaitInPoolTimeout); + registerRequestWaitInPoolTimeout(CoreConst.HIGHWAY, msHighwayRequestWaitInPoolTimeout); } public long getNanoHighwayRequestWaitInPoolTimeout() { @@ -182,7 +182,7 @@ public long getMsRestRequestWaitInPoolTimeout() { public void setMsRestRequestWaitInPoolTimeout(long msRestRequestWaitInPoolTimeout) { this.msRestRequestWaitInPoolTimeout = msRestRequestWaitInPoolTimeout; this.nanoRestRequestWaitInPoolTimeout = TimeUnit.MILLISECONDS.toNanos(msRestRequestWaitInPoolTimeout); - registerRequestWaitInPoolTimeout(Const.RESTFUL, msRestRequestWaitInPoolTimeout); + registerRequestWaitInPoolTimeout(CoreConst.RESTFUL, msRestRequestWaitInPoolTimeout); } public long getNanoRestRequestWaitInPoolTimeout() { @@ -224,7 +224,7 @@ public String getTransport() { public void setTransport(String transport) { if (transport == null) { - transport = Const.ANY_TRANSPORT; + transport = CoreConst.ANY_TRANSPORT; } this.transport = transport; } diff --git a/core/src/main/java/org/apache/servicecomb/core/definition/OperationMeta.java b/core/src/main/java/org/apache/servicecomb/core/definition/OperationMeta.java index 7f0eb06a30b..003daaffab1 100644 --- a/core/src/main/java/org/apache/servicecomb/core/definition/OperationMeta.java +++ b/core/src/main/java/org/apache/servicecomb/core/definition/OperationMeta.java @@ -18,13 +18,14 @@ import java.util.concurrent.Executor; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.foundation.common.VendorExtensions; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import org.apache.servicecomb.swagger.invocation.response.ResponsesMeta; -import io.swagger.models.Operation; +import io.swagger.v3.oas.models.Operation; + public class OperationMeta { private SchemaMeta schemaMeta; @@ -46,8 +47,6 @@ public class OperationMeta { private final ResponsesMeta responsesMeta = new ResponsesMeta(); - private OperationConfig config; - private final VendorExtensions vendorExtensions = new VendorExtensions(); public OperationMeta init(SchemaMeta schemaMeta, SwaggerOperation swaggerOperation) { @@ -59,22 +58,21 @@ public OperationMeta init(SchemaMeta schemaMeta, SwaggerOperation swaggerOperati this.operationPath = swaggerOperation.getPath(); this.swaggerOperation = swaggerOperation.getOperation(); this.executor = schemaMeta.getMicroserviceMeta().getScbEngine().getExecutorManager().findExecutor(this); - this.config = schemaMeta.getMicroserviceMeta().getMicroserviceVersionsMeta().getOrCreateOperationConfig(this); this.responsesMeta.init(schemaMeta.getSwagger(), swaggerOperation.getOperation()); return this; } public void setSwaggerProducerOperation(SwaggerProducerOperation swaggerProducerOperation) { - this.putExtData(Const.PRODUCER_OPERATION, swaggerProducerOperation); + this.putExtData(CoreConst.PRODUCER_OPERATION, swaggerProducerOperation); } public SwaggerProducerOperation getSwaggerProducerOperation() { - return (SwaggerProducerOperation) this.getExtData(Const.PRODUCER_OPERATION); + return (SwaggerProducerOperation) this.getExtData(CoreConst.PRODUCER_OPERATION); } public OperationConfig getConfig() { - return config; + return schemaMeta.getMicroserviceMeta().getMicroserviceVersionsMeta().getOrCreateOperationConfig(this); } public String getHttpMethod() { @@ -95,6 +93,17 @@ public ResponsesMeta getResponsesMeta() { return responsesMeta; } + public int parameterCount() { + int result = 0; + if (swaggerOperation.getRequestBody() != null) { + result++; + } + if (swaggerOperation.getParameters() != null) { + result += swaggerOperation.getParameters().size(); + } + return result; + } + private ResponsesMeta cloneResponseMeta() { ResponsesMeta result = new ResponsesMeta(); this.responsesMeta.cloneTo(result); diff --git a/core/src/main/java/org/apache/servicecomb/core/definition/SchemaMeta.java b/core/src/main/java/org/apache/servicecomb/core/definition/SchemaMeta.java index be6ad7b4b95..623ec695b0c 100644 --- a/core/src/main/java/org/apache/servicecomb/core/definition/SchemaMeta.java +++ b/core/src/main/java/org/apache/servicecomb/core/definition/SchemaMeta.java @@ -25,14 +25,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.swagger.models.Swagger; +import io.swagger.v3.oas.models.OpenAPI; public class SchemaMeta { private static final Logger LOGGER = LoggerFactory.getLogger(SchemaMeta.class); private final MicroserviceMeta microserviceMeta; - private final Swagger swagger; + private final OpenAPI swagger; private final String schemaId; @@ -43,7 +43,7 @@ public class SchemaMeta { private final VendorExtensions vendorExtensions = new VendorExtensions(); - public SchemaMeta(MicroserviceMeta microserviceMeta, String schemaId, Swagger swagger) { + public SchemaMeta(MicroserviceMeta microserviceMeta, String schemaId, OpenAPI swagger) { this.microserviceMeta = microserviceMeta; this.schemaId = schemaId; this.swagger = swagger; @@ -69,7 +69,7 @@ public MicroserviceMeta getMicroserviceMeta() { return microserviceMeta; } - public Swagger getSwagger() { + public OpenAPI getSwagger() { return swagger; } diff --git a/core/src/main/java/org/apache/servicecomb/core/definition/ServiceRegistryListener.java b/core/src/main/java/org/apache/servicecomb/core/definition/ServiceRegistryListener.java deleted file mode 100644 index 952efbaffb4..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/definition/ServiceRegistryListener.java +++ /dev/null @@ -1,135 +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.servicecomb.core.definition; - -import static org.apache.servicecomb.core.definition.CoreMetaUtils.CORE_MICROSERVICE_META; -import static org.apache.servicecomb.core.definition.CoreMetaUtils.CORE_MICROSERVICE_VERSION; -import static org.apache.servicecomb.core.definition.CoreMetaUtils.CORE_MICROSERVICE_VERSIONS_META; -import static org.apache.servicecomb.core.definition.CoreMetaUtils.getMicroserviceVersionsMeta; - -import javax.ws.rs.core.Response.Status; - -import org.apache.servicecomb.core.SCBEngine; -import org.apache.servicecomb.foundation.common.event.EnableExceptionPropagation; -import org.apache.servicecomb.foundation.common.event.SubscriberOrder; -import org.apache.servicecomb.registry.api.event.CreateMicroserviceEvent; -import org.apache.servicecomb.registry.api.event.CreateMicroserviceVersionEvent; -import org.apache.servicecomb.registry.api.event.DestroyMicroserviceEvent; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.consumer.MicroserviceVersion; -import org.apache.servicecomb.registry.consumer.MicroserviceVersions; -import org.apache.servicecomb.registry.definition.DefinitionConst; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.eventbus.Subscribe; -import com.netflix.config.DynamicPropertyFactory; - -import io.swagger.models.Swagger; - -/** - * subscribe event from ServiceRegistry module to create or destroy metas - */ -public class ServiceRegistryListener { - private static final Logger LOGGER = LoggerFactory.getLogger(ServiceRegistryListener.class); - - private static final String SHOULD_WOKR_WITH_THIRD_PARTY = "servicecomb.service.registry.workWithThirdParty"; - - private static final int SCHEMA_RETRY = 3; - - private final SCBEngine scbEngine; - - public ServiceRegistryListener(SCBEngine scbEngine) { - this.scbEngine = scbEngine; - scbEngine.getEventBus().register(this); - } - - @EnableExceptionPropagation - @SubscriberOrder(-1000) - @Subscribe - public void onCreateMicroservice(CreateMicroserviceEvent event) { - MicroserviceVersions microserviceVersions = event.getMicroserviceVersions(); - microserviceVersions.getVendorExtensions() - .put(CORE_MICROSERVICE_VERSIONS_META, - new ConsumerMicroserviceVersionsMeta(scbEngine, microserviceVersions)); - } - - @EnableExceptionPropagation - @SubscriberOrder(-1000) - @Subscribe - public void onDestroyMicroservice(DestroyMicroserviceEvent event) { - - } - - @EnableExceptionPropagation - @SubscriberOrder(-1000) - @Subscribe - public void onCreateMicroserviceVersion(CreateMicroserviceVersionEvent event) { - MicroserviceVersion microserviceVersion = event.getMicroserviceVersion(); - Microservice microservice = microserviceVersion.getMicroservice(); - - // not shortName, to support cross app invoke - String microserviceName = microserviceVersion.getMicroserviceName(); - MicroserviceMeta microserviceMeta = new MicroserviceMeta(scbEngine, microserviceName, true); - microserviceMeta.setFilterChain(scbEngine.getFilterChainsManager().findConsumerChain(microserviceName)); - - MicroserviceVersions microserviceVersions = microserviceVersion.getMicroserviceVersions(); - microserviceMeta.setMicroserviceVersionsMeta(getMicroserviceVersionsMeta(microserviceVersions)); - - boolean isServiceCenter = DefinitionConst.REGISTRY_APP_ID.equals(microservice.getAppId()) - && DefinitionConst.REGISTRY_SERVICE_NAME.equals(microservice.getServiceName()); - // do not load service center schemas, because service center did not provide swagger,but can get schema ids.... - // service center better to resolve the problem. - if (!isServiceCenter) { - for (String schemaId : microservice.getSchemas()) { - for (int i = 0; i <= SCHEMA_RETRY; i++) { - Swagger swagger = scbEngine.getSwaggerLoader() - .loadSwagger(microservice, microserviceVersion.getInstances(), schemaId); - if (swagger != null) { - microserviceMeta.registerSchemaMeta(schemaId, swagger); - break; - } - // scenario1: - // allow users register schemaId, but without schema contents. This is useful when cooperate with other - // non java-chassis framework. - // scenario2: - // query schema content failed from service center due to some network problems. - - // We do not know if this service do not have schema content or failed to access service center. - // And if we need know this, a lot of API in ServiceRegistryClient need refactored. - // So we add a configuration if users know will not work with others like Dubbo , or just do a retry - // to query the schema to balance the two scenarios. - if (!DynamicPropertyFactory.getInstance().getBooleanProperty(SHOULD_WOKR_WITH_THIRD_PARTY, true).get()) { - throw new InvocationException(Status.INTERNAL_SERVER_ERROR, - "swagger can not be empty or load swagger failed"); - } - - // So we only give a retry to query the schema to balance the two scenarios. - LOGGER.warn("load schema id {} failed, and retry times is {}", schemaId, i); - } - } - } - - microserviceMeta.putExtData(CORE_MICROSERVICE_VERSION, microserviceVersion); - microserviceVersion.getVendorExtensions().put(CORE_MICROSERVICE_META, microserviceMeta); - } - - public void destroy() { - scbEngine.getEventBus().unregister(this); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationFinishEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationFinishEvent.java index d1d96b4d1b5..21a20226d01 100644 --- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationFinishEvent.java +++ b/core/src/main/java/org/apache/servicecomb/core/event/InvocationFinishEvent.java @@ -20,14 +20,7 @@ import org.apache.servicecomb.swagger.invocation.Response; public class InvocationFinishEvent extends InvocationWithResponseEvent { - private final long nanoCurrent; - public InvocationFinishEvent(Invocation invocation, Response response) { super(invocation, response); - this.nanoCurrent = invocation.getInvocationStageTrace().getFinish(); - } - - public long getNanoCurrent() { - return nanoCurrent; } } diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationHandlersStartEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationHandlersStartEvent.java deleted file mode 100644 index 55d57001b89..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationHandlersStartEvent.java +++ /dev/null @@ -1,26 +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.servicecomb.core.event; - -import org.apache.servicecomb.core.Invocation; - -public class InvocationHandlersStartEvent extends InvocationBaseEvent { - public InvocationHandlersStartEvent(Invocation invocation) { - super(invocation); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationRunInExecutorFinishEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationRunInExecutorFinishEvent.java deleted file mode 100644 index 73ee6cd6dae..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationRunInExecutorFinishEvent.java +++ /dev/null @@ -1,28 +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.servicecomb.core.event; - -import org.apache.servicecomb.core.Invocation; - -/** - * for async invocation, only indicate finished in executor, not finished invocation - */ -public class InvocationRunInExecutorFinishEvent extends InvocationBaseEvent { - public InvocationRunInExecutorFinishEvent(Invocation invocation) { - super(invocation); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationRunInExecutorStartEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationRunInExecutorStartEvent.java deleted file mode 100644 index 3779bde03f5..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationRunInExecutorStartEvent.java +++ /dev/null @@ -1,25 +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.servicecomb.core.event; - -import org.apache.servicecomb.core.Invocation; - -public class InvocationRunInExecutorStartEvent extends InvocationBaseEvent { - public InvocationRunInExecutorStartEvent(Invocation invocation) { - super(invocation); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/CoreExceptionConfiguration.java b/core/src/main/java/org/apache/servicecomb/core/exception/CoreExceptionConfiguration.java index d7ec21b1f4f..856888764a1 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/CoreExceptionConfiguration.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/CoreExceptionConfiguration.java @@ -16,15 +16,18 @@ */ package org.apache.servicecomb.core.exception; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CoreExceptionConfiguration { - public Exceptions exceptions() { + @Bean + public Exceptions scbExceptions() { return new Exceptions(); } - public DefaultExceptionProcessor defaultExceptionProcessor() { + @Bean + public DefaultExceptionProcessor scbDefaultExceptionProcessor() { return new DefaultExceptionProcessor(); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java b/core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java index 5d37de247c6..e8817af521d 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java @@ -17,9 +17,9 @@ package org.apache.servicecomb.core.exception; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; -import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; -import static javax.ws.rs.core.Response.Status.TOO_MANY_REQUESTS; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; +import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; +import static jakarta.ws.rs.core.Response.Status.TOO_MANY_REQUESTS; import static org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace; import static org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_SERVER; import static org.apache.servicecomb.swagger.invocation.InvocationType.CONSUMER; @@ -29,13 +29,10 @@ import java.util.Map; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.ws.rs.core.Response.StatusType; - import org.apache.servicecomb.config.inject.InjectProperties; import org.apache.servicecomb.config.inject.InjectProperty; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.tracing.TraceIdLogger; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.utils.ExceptionUtils; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; @@ -49,6 +46,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.ws.rs.core.Response.StatusType; + @InjectProperties(prefix = "servicecomb.invocation.exception") public class DefaultExceptionProcessor implements ExceptionProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExceptionProcessor.class); @@ -62,7 +61,7 @@ public class DefaultExceptionProcessor implements ExceptionProcessor { .map(converter -> (ExceptionConverter) converter) .collect(Collectors.toList()); - @InjectProperty(keys = "print-stack-trace", defaultValue = "false") + @InjectProperty(keys = "print-stack-trace", defaultValue = "true") protected boolean printStackTrace; @InjectProperty(keys = "print-rate-limit", defaultValue = "false") @@ -104,17 +103,29 @@ public DefaultExceptionProcessor setConverters(List findConverter(unwrapped)) - .convert(invocation, unwrapped, genericStatus); + ExceptionConverter converter = + converterCache.computeIfAbsent(unwrapped.getClass(), clazz -> findConverter(unwrapped)); + if (invocation == null) { + LOGGER.warn("Convert unknown operation exception {}/{} using {}.", + throwable.getClass().getSimpleName(), unwrapped.getClass().getSimpleName(), + converter.getClass().getSimpleName()); + } else { + invocation.getTraceIdLogger().warn("{} Convert operation {} exception {}/{} using {}.", + TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), + invocation.getMicroserviceQualifiedName(), + throwable.getClass().getSimpleName(), unwrapped.getClass().getSimpleName(), + converter.getClass().getSimpleName()); + } + return converter.convert(invocation, unwrapped, genericStatus); } catch (Exception e) { LOGGER.error("BUG: ExceptionConverter.convert MUST not throw exception, please fix it.\n" + "original exception :{}" @@ -144,29 +155,29 @@ public Response toConsumerResponse(Invocation invocation, Throwable throwable) { } @Override - public void logConsumerException(@Nonnull Invocation invocation, @Nonnull InvocationException exception) { + public void logConsumerException(Invocation invocation, InvocationException exception) { if (isIgnoreLog(invocation, exception)) { return; } if (isPrintStackTrace()) { - LOGGER.error("failed to invoke {}, endpoint={}, trace id={}.", + invocation.getTraceIdLogger().error("{} Failed to invoke {}, endpoint={}.", + TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), invocation.getMicroserviceQualifiedName(), invocation.getEndpoint(), - invocation.getTraceId(), exception); return; } - LOGGER.error("failed to invoke {}, endpoint={}, trace id={}, message={}.", + invocation.getTraceIdLogger().error("{} Failed to invoke {}, endpoint={}, message={}.", + TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), invocation.getMicroserviceQualifiedName(), invocation.getEndpoint(), - invocation.getTraceId(), ExceptionUtils.getExceptionMessageWithoutTrace(exception)); } @Override - public boolean isIgnoreLog(@Nonnull Invocation invocation, @Nonnull InvocationException exception) { + public boolean isIgnoreLog(Invocation invocation, InvocationException exception) { if (!isPrintRateLimit() && exception.getStatusCode() == TOO_MANY_REQUESTS.getStatusCode()) { return true; } @@ -175,28 +186,29 @@ public boolean isIgnoreLog(@Nonnull Invocation invocation, @Nonnull InvocationEx } @Override - public Response toProducerResponse(@Nullable Invocation invocation, Throwable exception) { + public Response toProducerResponse(Invocation invocation, Throwable exception) { InvocationException invocationException = convert(invocation, exception, INTERNAL_SERVER_ERROR); if (invocation != null) { logProducerException(invocation, invocationException); } - return Response.status(invocationException.getStatus()) - .entity(invocationException.getErrorData()); + return Response.createFail(invocationException); } @Override - public void logProducerException(@Nonnull Invocation invocation, @Nonnull InvocationException exception) { + public void logProducerException(Invocation invocation, InvocationException exception) { if (isIgnoreLog(invocation, exception)) { return; } if (isPrintStackTrace()) { - LOGGER.error("failed to process {} invocation, operation={}.", + invocation.getTraceIdLogger().error("{} Failed to process {} invocation, operation={}.", + TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), invocation.getInvocationType(), invocation.getMicroserviceQualifiedName(), exception); return; } - LOGGER.error("failed to process {} invocation, operation={}, message={}.", + invocation.getTraceIdLogger().error("{} Failed to process {} invocation, operation={}, message={}.", + TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), invocation.getInvocationType(), invocation.getMicroserviceQualifiedName(), ExceptionUtils.getExceptionMessageWithoutTrace(exception)); } diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionConverter.java b/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionConverter.java index eedfa432523..b6424564747 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionConverter.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionConverter.java @@ -16,17 +16,16 @@ */ package org.apache.servicecomb.core.exception; -import static javax.ws.rs.core.Response.Status.Family.CLIENT_ERROR; +import static jakarta.ws.rs.core.Response.Status.Family.CLIENT_ERROR; import static org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_CLIENT; import static org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_SERVER; -import javax.annotation.Nullable; -import javax.ws.rs.core.Response.StatusType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.utils.SPIOrder; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import jakarta.ws.rs.core.Response.StatusType; + public interface ExceptionConverter extends SPIOrder { static boolean isClient(StatusType status) { return CLIENT_ERROR.equals(status.getFamily()); @@ -50,5 +49,5 @@ static String getGenericCode(StatusType status) { * @param genericStatus if can not determine the status type, then use this input value * @return converted invocation exception */ - InvocationException convert(@Nullable Invocation invocation, T throwable, StatusType genericStatus); + InvocationException convert(Invocation invocation, T throwable, StatusType genericStatus); } diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionProcessor.java b/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionProcessor.java index 859c7a71daa..ffb3619bb41 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionProcessor.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionProcessor.java @@ -17,15 +17,13 @@ package org.apache.servicecomb.core.exception; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.ws.rs.core.Response.StatusType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.core.Ordered; +import jakarta.ws.rs.core.Response.StatusType; + /** * will select the min order instance */ @@ -37,17 +35,17 @@ default int getOrder() { boolean isPrintStackTrace(); - InvocationException convert(@Nonnull Invocation invocation, Throwable throwable); + InvocationException convert(Invocation invocation, Throwable throwable); - InvocationException convert(@Nullable Invocation invocation, Throwable throwable, StatusType genericStatus); + InvocationException convert(Invocation invocation, Throwable throwable, StatusType genericStatus); - boolean isIgnoreLog(@Nonnull Invocation invocation, @Nonnull InvocationException exception); + boolean isIgnoreLog(Invocation invocation, InvocationException exception); Response toConsumerResponse(Invocation invocation, Throwable throwable); - void logConsumerException(@Nonnull Invocation invocation, @Nonnull InvocationException exception); + void logConsumerException(Invocation invocation, InvocationException exception); - Response toProducerResponse(@Nullable Invocation invocation, Throwable exception); + Response toProducerResponse(Invocation invocation, Throwable exception); - void logProducerException(@Nonnull Invocation invocation, @Nonnull InvocationException exception); + void logProducerException(Invocation invocation, InvocationException exception); } diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/Exceptions.java b/core/src/main/java/org/apache/servicecomb/core/exception/Exceptions.java index aff3e1e6066..0fb8ad418a1 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/Exceptions.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/Exceptions.java @@ -17,8 +17,8 @@ package org.apache.servicecomb.core.exception; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; -import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; +import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_CLIENT; import static org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_SERVER; import static org.apache.servicecomb.swagger.invocation.InvocationType.CONSUMER; @@ -26,16 +26,14 @@ import java.util.Comparator; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.ws.rs.core.Response.StatusType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.beans.factory.annotation.Autowired; +import jakarta.ws.rs.core.Response.StatusType; + public class Exceptions { private static ExceptionProcessor processor = new DefaultExceptionProcessor(); @@ -102,7 +100,7 @@ public static InvocationException genericProducer(String msg, Throwable cause) { return producer(GENERIC_SERVER, msg, cause); } - public static StatusType getGenericStatus(@Nonnull Invocation invocation) { + public static StatusType getGenericStatus(Invocation invocation) { return CONSUMER.equals(invocation.getInvocationType()) ? BAD_REQUEST : INTERNAL_SERVER_ERROR; } @@ -110,15 +108,15 @@ public static Response toConsumerResponse(Invocation invocation, Throwable throw return processor.toConsumerResponse(invocation, throwable); } - public static Response toProducerResponse(@Nullable Invocation invocation, Throwable exception) { + public static Response toProducerResponse(Invocation invocation, Throwable exception) { return processor.toProducerResponse(invocation, exception); } - public static InvocationException convert(@Nonnull Invocation invocation, Throwable throwable) { + public static InvocationException convert(Invocation invocation, Throwable throwable) { return processor.convert(invocation, throwable); } - public static InvocationException convert(@Nullable Invocation invocation, Throwable throwable, + public static InvocationException convert(Invocation invocation, Throwable throwable, StatusType genericStatus) { return processor.convert(invocation, throwable, genericStatus); } diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/IllegalArgumentExceptionToProducerResponseConverter.java b/core/src/main/java/org/apache/servicecomb/core/exception/IllegalArgumentExceptionToProducerResponseConverter.java deleted file mode 100644 index c3c8d2e9f84..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/exception/IllegalArgumentExceptionToProducerResponseConverter.java +++ /dev/null @@ -1,46 +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.servicecomb.core.exception; - -import javax.ws.rs.core.Response.Status; - -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; -import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; -import org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; - -public class IllegalArgumentExceptionToProducerResponseConverter implements - ExceptionToProducerResponseConverter { - @Override - public Class getExceptionClass() { - return IllegalArgumentException.class; - } - - @Override - public int getOrder() { - return 10000; - } - - @Override - public Response convert(SwaggerInvocation swaggerInvocation, IllegalArgumentException e) { - InvocationException invocationException = new InvocationException(Status.BAD_REQUEST.getStatusCode(), "", - new CommonExceptionData("Parameters not valid or types not match."), e); - return Response.failResp(invocationException); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/ThrowableExceptionToProducerResponseConverter.java b/core/src/main/java/org/apache/servicecomb/core/exception/ThrowableExceptionToProducerResponseConverter.java deleted file mode 100644 index 4ffa2ec9e99..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/exception/ThrowableExceptionToProducerResponseConverter.java +++ /dev/null @@ -1,45 +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.servicecomb.core.exception; - -import javax.ws.rs.core.Response.Status; - -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; -import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; -import org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; - -public class ThrowableExceptionToProducerResponseConverter implements ExceptionToProducerResponseConverter { - @Override - public Class getExceptionClass() { - return Throwable.class; - } - - @Override - public int getOrder() { - return 20000; - } - - @Override - public Response convert(SwaggerInvocation swaggerInvocation, Throwable e) { - InvocationException invocationException = new InvocationException(Status.INTERNAL_SERVER_ERROR.getStatusCode(), "", - new CommonExceptionData("Unexpected exception when processing the request."), e); - return Response.failResp(invocationException); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/converter/ConnectTimeoutExceptionConverter.java b/core/src/main/java/org/apache/servicecomb/core/exception/converter/ConnectTimeoutExceptionConverter.java index 961eb4f477d..72507d0a3ba 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/converter/ConnectTimeoutExceptionConverter.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/ConnectTimeoutExceptionConverter.java @@ -16,10 +16,7 @@ */ package org.apache.servicecomb.core.exception.converter; -import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; - -import javax.annotation.Nullable; -import javax.ws.rs.core.Response.StatusType; +import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; @@ -28,6 +25,7 @@ import org.slf4j.LoggerFactory; import io.netty.channel.ConnectTimeoutException; +import jakarta.ws.rs.core.Response.StatusType; public class ConnectTimeoutExceptionConverter implements ExceptionConverter { private static final Logger LOGGER = LoggerFactory.getLogger(ConnectTimeoutExceptionConverter.class); @@ -45,7 +43,7 @@ public boolean canConvert(Throwable throwable) { } @Override - public InvocationException convert(@Nullable Invocation invocation, ConnectTimeoutException throwable, + public InvocationException convert(Invocation invocation, ConnectTimeoutException throwable, StatusType genericStatus) { // throwable.getMessage: // connection timed out: /1.1.1.1:8080 diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/converter/ConstraintViolationExceptionConverter.java b/core/src/main/java/org/apache/servicecomb/core/exception/converter/ConstraintViolationExceptionConverter.java index c2e08344ef4..2898d4914ae 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/converter/ConstraintViolationExceptionConverter.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/ConstraintViolationExceptionConverter.java @@ -16,41 +16,27 @@ */ package org.apache.servicecomb.core.exception.converter; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static org.apache.servicecomb.core.exception.ExceptionCodes.DEFAULT_VALIDATE; import java.util.List; import java.util.stream.Collectors; -import javax.validation.ConstraintViolationException; -import javax.ws.rs.core.Response.StatusType; - import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import com.google.common.annotations.VisibleForTesting; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicStringProperty; +import jakarta.validation.ConstraintViolationException; +import jakarta.ws.rs.core.Response.StatusType; public class ConstraintViolationExceptionConverter implements ExceptionConverter { public static final int ORDER = Short.MAX_VALUE; public static final String KEY_CODE = "servicecomb.filters.validate.code"; - private DynamicStringProperty code; - public ConstraintViolationExceptionConverter() { - refreshCode(); - } - - /** - * during UT, DynamicPropertyFactory will be reset, this caused code can not changed by event - */ - @VisibleForTesting - public void refreshCode() { - code = DynamicPropertyFactory.getInstance().getStringProperty(KEY_CODE, DEFAULT_VALIDATE); } @Override @@ -70,7 +56,8 @@ public InvocationException convert(Invocation invocation, ConstraintViolationExc .map(violation -> new ValidateDetail(violation.getPropertyPath().toString(), violation.getMessage())) .collect(Collectors.toList()); - CommonExceptionData exceptionData = new CommonExceptionData(code.get(), "invalid parameters."); + CommonExceptionData exceptionData = new CommonExceptionData(SCBEngine.getInstance().getEnvironment(). + getProperty(KEY_CODE, String.class, DEFAULT_VALIDATE), "invalid parameters."); exceptionData.putDynamic("validateDetail", details); return new InvocationException(BAD_REQUEST, exceptionData); } diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/converter/DefaultExceptionConverter.java b/core/src/main/java/org/apache/servicecomb/core/exception/converter/DefaultExceptionConverter.java index b48bdb4e609..d0338d43628 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/converter/DefaultExceptionConverter.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/DefaultExceptionConverter.java @@ -16,14 +16,11 @@ */ package org.apache.servicecomb.core.exception.converter; -import javax.annotation.Nullable; -import javax.ws.rs.core.Response.StatusType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + +import jakarta.ws.rs.core.Response.StatusType; /** *
@@ -38,8 +35,6 @@
  * 
*/ public class DefaultExceptionConverter implements ExceptionConverter { - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExceptionConverter.class); - public static final int ORDER = Integer.MAX_VALUE; @Override @@ -53,14 +48,9 @@ public boolean canConvert(Throwable throwable) { } @Override - public InvocationException convert(@Nullable Invocation invocation, Throwable throwable, StatusType genericStatus) { - String msg = throwable.getMessage(); - if (msg == null) { - msg = "Unexpected exception when processing."; - } - - LOGGER.error("convert unknown exception({}) to InvocationException, message={}.", - throwable.getClass().getName(), msg); + public InvocationException convert(Invocation invocation, Throwable throwable, StatusType genericStatus) { + String msg = String.format("Unexpected exception when processing %s. %s", + invocation == null ? "none" : invocation.getMicroserviceQualifiedName(), throwable.getMessage()); return new InvocationException(genericStatus, ExceptionConverter.getGenericCode(genericStatus), msg, throwable); } diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/converter/IllegalArgumentExceptionConverter.java b/core/src/main/java/org/apache/servicecomb/core/exception/converter/IllegalArgumentExceptionConverter.java index 8725ce13679..f80f1a82519 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/converter/IllegalArgumentExceptionConverter.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/IllegalArgumentExceptionConverter.java @@ -16,13 +16,12 @@ */ package org.apache.servicecomb.core.exception.converter; -import javax.annotation.Nullable; -import javax.ws.rs.core.Response.StatusType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import jakarta.ws.rs.core.Response.StatusType; + public class IllegalArgumentExceptionConverter implements ExceptionConverter { public static final int ORDER = Short.MAX_VALUE; @@ -37,7 +36,7 @@ public boolean canConvert(Throwable throwable) { } @Override - public InvocationException convert(@Nullable Invocation invocation, IllegalArgumentException throwable, + public InvocationException convert(Invocation invocation, IllegalArgumentException throwable, StatusType genericStatus) { return new InvocationException(genericStatus, ExceptionConverter.getGenericCode(genericStatus), "Parameters not valid or types not match.", throwable); diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/converter/InvocationExceptionConverter.java b/core/src/main/java/org/apache/servicecomb/core/exception/converter/InvocationExceptionConverter.java index 0a7d2714b79..830aec3190c 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/converter/InvocationExceptionConverter.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/InvocationExceptionConverter.java @@ -16,13 +16,12 @@ */ package org.apache.servicecomb.core.exception.converter; -import javax.annotation.Nullable; -import javax.ws.rs.core.Response.StatusType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import jakarta.ws.rs.core.Response.StatusType; + public class InvocationExceptionConverter implements ExceptionConverter { public static final int ORDER = Byte.MAX_VALUE; @@ -37,7 +36,7 @@ public boolean canConvert(Throwable throwable) { } @Override - public InvocationException convert(@Nullable Invocation invocation, InvocationException throwable, + public InvocationException convert(Invocation invocation, InvocationException throwable, StatusType genericStatus) { return throwable; } diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/converter/ServiceCombExceptionConverter.java b/core/src/main/java/org/apache/servicecomb/core/exception/converter/ServiceCombExceptionConverter.java index aecee710756..9a69f4c988c 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/converter/ServiceCombExceptionConverter.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/ServiceCombExceptionConverter.java @@ -19,15 +19,14 @@ import java.util.concurrent.TimeoutException; -import javax.annotation.Nullable; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.Response.StatusType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.StatusType; + public class ServiceCombExceptionConverter implements ExceptionConverter { public static final int ORDER = Byte.MAX_VALUE; @@ -44,7 +43,7 @@ public boolean canConvert(Throwable throwable) { } @Override - public InvocationException convert(@Nullable Invocation invocation, ServiceCombException throwable, + public InvocationException convert(Invocation invocation, ServiceCombException throwable, StatusType genericStatus) { if (throwable.getCause() instanceof TimeoutException) { return timeoutExceptionConverter.convert(invocation, (TimeoutException) throwable.getCause(), genericStatus); diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/converter/TimeoutExceptionConverter.java b/core/src/main/java/org/apache/servicecomb/core/exception/converter/TimeoutExceptionConverter.java index 0059e7f4fe5..31d48a82933 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/converter/TimeoutExceptionConverter.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/TimeoutExceptionConverter.java @@ -16,19 +16,18 @@ */ package org.apache.servicecomb.core.exception.converter; -import static javax.ws.rs.core.Response.Status.REQUEST_TIMEOUT; +import static jakarta.ws.rs.core.Response.Status.REQUEST_TIMEOUT; import java.util.concurrent.TimeoutException; -import javax.annotation.Nullable; -import javax.ws.rs.core.Response.StatusType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jakarta.ws.rs.core.Response.StatusType; + public class TimeoutExceptionConverter implements ExceptionConverter { private static final Logger LOGGER = LoggerFactory.getLogger(TimeoutExceptionConverter.class); @@ -45,7 +44,7 @@ public boolean canConvert(Throwable throwable) { } @Override - public InvocationException convert(@Nullable Invocation invocation, TimeoutException throwable, + public InvocationException convert(Invocation invocation, TimeoutException throwable, StatusType genericStatus) { // throwable.getMessage: // The timeout period of 30000ms has been exceeded while executing GET /xxx for server 1.1.1.1:8080 diff --git a/core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java b/core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java index ad97964d300..f03547c171a 100644 --- a/core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java +++ b/core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java @@ -23,26 +23,37 @@ import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; -import com.netflix.config.DynamicPropertyFactory; - -public class ExecutorManager { +public final class ExecutorManager { public static final String KEY_EXECUTORS_PREFIX = "servicecomb.executors.Provider."; public static final String KEY_EXECUTORS_DEFAULT = "servicecomb.executors.default"; - public static final String EXECUTOR_GROUP_THREADPOOL = "servicecomb.executor.groupThreadPool"; + public static final String EXECUTOR_GROUP_THREAD_POOL = "servicecomb.executor.groupThreadPool"; public static final String EXECUTOR_REACTIVE = "servicecomb.executor.reactive"; - public static final String EXECUTOR_DEFAULT = EXECUTOR_GROUP_THREADPOOL; - private final Map executors = new ConcurrentHashMapEx<>(); + static String EXECUTOR_DEFAULT = EXECUTOR_GROUP_THREAD_POOL; + + public static void setExecutorDefault(String executorDefault) { + EXECUTOR_DEFAULT = executorDefault; + } + + private Environment environment; + public ExecutorManager() { registerExecutor(EXECUTOR_REACTIVE, new ReactiveExecutor()); } + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + public void registerExecutor(String id, Executor executor) { Executor existing = executors.putIfAbsent(id, executor); if (existing != null) { @@ -100,8 +111,8 @@ public Executor findExecutor(OperationMeta operationMeta, Executor defaultOperat return findExecutorById(EXECUTOR_DEFAULT); } - protected Executor findByKey(String configKey) { - String id = DynamicPropertyFactory.getInstance().getStringProperty(configKey, null).get(); + private Executor findByKey(String configKey) { + String id = environment.getProperty(configKey); if (id == null) { return null; } diff --git a/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java b/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java index a3fd1c8fc93..13562914fac 100644 --- a/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java +++ b/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java @@ -30,8 +30,7 @@ import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - -import com.netflix.config.DynamicPropertyFactory; +import org.springframework.core.env.Environment; public class GroupExecutor implements Executor, Closeable { private static final Logger LOGGER = LoggerFactory.getLogger(GroupExecutor.class); @@ -51,6 +50,8 @@ public class GroupExecutor implements Executor, Closeable { private static final AtomicBoolean LOG_PRINTED = new AtomicBoolean(); + private final Environment environment; + protected String groupName; protected int groupCount; @@ -72,6 +73,10 @@ public class GroupExecutor implements Executor, Closeable { private final Map threadExecutorMap = new ConcurrentHashMapEx<>(); + public GroupExecutor(Environment environment) { + this.environment = environment; + } + public GroupExecutor init() { return init("group"); } @@ -95,7 +100,7 @@ public GroupExecutor init(String groupName) { return this; } - public void initConfig() { + public synchronized void initConfig() { if (LOG_PRINTED.compareAndSet(false, true)) { LOGGER.info("thread pool rules:\n" + "1.use core threads.\n" @@ -104,12 +109,12 @@ public void initConfig() { + "4.if queue is full, and threads count is max, then reject the request."); } - groupCount = DynamicPropertyFactory.getInstance().getIntProperty(KEY_GROUP, 2).get(); - coreThreads = DynamicPropertyFactory.getInstance().getIntProperty(KEY_CORE_THREADS, 25).get(); + groupCount = environment.getProperty(KEY_GROUP, int.class, 2); + coreThreads = environment.getProperty(KEY_CORE_THREADS, int.class, 25); - maxThreads = DynamicPropertyFactory.getInstance().getIntProperty(KEY_MAX_THREADS, -1).get(); + maxThreads = environment.getProperty(KEY_MAX_THREADS, int.class, -1); if (maxThreads <= 0) { - maxThreads = DynamicPropertyFactory.getInstance().getIntProperty(KEY_OLD_MAX_THREAD, -1).get(); + maxThreads = environment.getProperty(KEY_OLD_MAX_THREAD, int.class, -1); if (maxThreads > 0) { LOGGER.warn("{} is deprecated, recommended to use {}.", KEY_OLD_MAX_THREAD, KEY_MAX_THREADS); } else { @@ -121,8 +126,8 @@ public void initConfig() { coreThreads = maxThreads; } - maxIdleInSecond = DynamicPropertyFactory.getInstance().getIntProperty(KEY_MAX_IDLE_SECOND, 60).get(); - maxQueueSize = DynamicPropertyFactory.getInstance().getIntProperty(KEY_MAX_QUEUE_SIZE, Integer.MAX_VALUE).get(); + maxIdleInSecond = environment.getProperty(KEY_MAX_IDLE_SECOND, int.class, 60); + maxQueueSize = environment.getProperty(KEY_MAX_QUEUE_SIZE, int.class, Integer.MAX_VALUE); LOGGER.info( "executor name={}, group={}. per group settings, coreThreads={}, maxThreads={}, maxIdleInSecond={}, maxQueueSize={}.", diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/AbstractFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/AbstractFilter.java new file mode 100644 index 00000000000..17d555fc8d1 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/AbstractFilter.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.filter; + +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; + +public abstract class AbstractFilter implements Filter, EnvironmentAware { + private static final String ORDER_KEY = "servicecomb.filter.%s.%s.%s.order"; + + private static final String ENABLE_KEY = "servicecomb.filter.%s.%s.%s.enabled"; + + protected Environment environment; + + private String nameWithOrder; + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public int getOrder(String application, String serviceName) { + Integer custom = environment.getProperty(String.format(ORDER_KEY, getName(), application, serviceName), + Integer.class); + if (custom != null) { + return custom; + } + return getOrder(); + } + + @Override + public boolean enabledForMicroservice(String application, String serviceName) { + Boolean custom = environment.getProperty(String.format(ENABLE_KEY, getName(), application, serviceName), + Boolean.class); + if (custom != null) { + return custom; + } + return true; + } + + @Override + public String getNameWithOrder() { + if (nameWithOrder == null) { + nameWithOrder = String.format("F(%1$06d)-%2$s", getOrder(), getName()); + } + return nameWithOrder; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.java index 899286787d9..de13aa0ac2e 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.java @@ -16,14 +16,6 @@ */ package org.apache.servicecomb.core.filter; -import javax.annotation.Nonnull; - -import org.apache.servicecomb.swagger.invocation.InvocationType; - public interface ConsumerFilter extends Filter { - @Nonnull - @Override - default boolean isEnabledForInvocationType(InvocationType invocationType) { - return invocationType == InvocationType.CONSUMER; - } + } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java b/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java index 0d383075cdd..0d9ce402624 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java @@ -16,38 +16,45 @@ */ package org.apache.servicecomb.core.filter; +import org.apache.servicecomb.core.filter.impl.ContextMapperFilter; import org.apache.servicecomb.core.filter.impl.ParameterValidatorFilter; -import org.apache.servicecomb.core.filter.impl.ProducerOperationFilter; +import org.apache.servicecomb.core.filter.impl.ProviderOperationFilter; +import org.apache.servicecomb.core.filter.impl.RetryFilter; import org.apache.servicecomb.core.filter.impl.ScheduleFilter; +import org.apache.servicecomb.governance.handler.MapperHandler; +import org.apache.servicecomb.governance.handler.RetryHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CoreFilterConfiguration { - //TODO: need remove all component scan or will cause bean conflict with load balance module -// @Bean -// @ConditionalOnMissingBean(name = "loadBalanceFilter") -// public ConsumerFilter loadBalanceFilter() { -// return new SimpleLoadBalanceFilter(); -// } - @Bean - public ProducerFilter producerOperationFilter() { - return new ProducerOperationFilter(); + public ProviderOperationFilter scbProducerOperationFilter() { + return new ProviderOperationFilter(); } @Bean - public ProducerFilter scheduleFilter() { + public ScheduleFilter scbScheduleFilter() { return new ScheduleFilter(); } @Bean - public FilterChainsManager filterChainsManager() { + public RetryFilter scbRetryFilter(RetryHandler retryHandler) { + return new RetryFilter(retryHandler); + } + + @Bean + public ContextMapperFilter scbContextMapperFilter(MapperHandler mapperHandler) { + return new ContextMapperFilter(mapperHandler); + } + + @Bean + public FilterChainsManager scbFilterChainsManager() { return new FilterChainsManager(); } @Bean - public ProducerFilter parameterValidatorFilter() { + public ParameterValidatorFilter scbParameterValidatorFilter() { return new ParameterValidatorFilter(); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/EdgeFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/EdgeFilter.java new file mode 100644 index 00000000000..70d8bef4ef5 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/EdgeFilter.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.filter; + +public interface EdgeFilter extends Filter { + +} diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/Filter.java b/core/src/main/java/org/apache/servicecomb/core/filter/Filter.java index eadf0e0a497..99ed798e686 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/Filter.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/Filter.java @@ -18,22 +18,16 @@ import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.provider.consumer.InvokerUtils; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; +import org.springframework.core.Ordered; /** *
- * unified extension for replace old version extensions:
- *   1. Handler
- *   2. HttpClientFilter
- *   3. HttpServerFilter
+ *  Filters are the basics of how an invocation is executed.
  *
  * thread rule:
- *   assume a producer filter chains is: f1, f2, schedule, f3, f4
+ *   assume a provider filter chains is: f1, f2, schedule, f3, f4
  *
  *   schedule is a builtIn filter, which will dispatch invocations to operation related threadPool
  *
@@ -53,37 +47,33 @@
  *        (reactive golden rule)
  * 
*/ -public interface Filter { - int PRODUCER_SCHEDULE_FILTER_ORDER = 0; +public interface Filter extends Ordered { + int PROVIDER_SCHEDULE_FILTER_ORDER = 0; int CONSUMER_LOAD_BALANCE_ORDER = 0; - default boolean isEnabledForInvocationType(InvocationType invocationType) { - return true; - } - - default boolean isEnabledForTransport(String transport) { + default boolean enabledForTransport(String transport) { return true; } - default boolean isEnabledForMicroservice(String microservice) { - //TODO read configuration and check if filter enabled. + default boolean enabledForMicroservice(String application, String serviceName) { return true; } - default int getOrder(InvocationType invocationType, String microservice) { + default int getOrder(String application, String serviceName) { return 0; } - default boolean isInEventLoop() { - return InvokerUtils.isInEventLoop(); + default int getOrder() { + return 0; } - @Nonnull default String getName() { - throw new IllegalStateException("must provide filter name."); + throw new IllegalStateException("must provide unique filter name."); } + String getNameWithOrder(); + /** * * @param invocation invocation @@ -94,7 +84,8 @@ default String getName() { * all fail data can only express by exception
*
* special for producer:
- * if response is failure, then after encode response, response.result will be exception.errorData, not a exception + * if response is failure, then after encode response, response.result will + * be exception.errorData, not a exception */ CompletableFuture onFilter(Invocation invocation, FilterNode nextNode); } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java b/core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java index 72b895cad15..2cc6281d018 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java @@ -22,26 +22,30 @@ import java.util.List; import java.util.stream.Collectors; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.springframework.beans.factory.annotation.Autowired; public class FilterChainsManager { - private final InvocationFilterChains consumerChains = new InvocationFilterChains(InvocationType.CONSUMER); + private InvocationFilterChains consumerChains; - private final InvocationFilterChains producerChains = new InvocationFilterChains(InvocationType.PRODUCER); + private InvocationFilterChains providerChains; + + private InvocationFilterChains edgeChains; @Autowired - public FilterChainsManager addFilters(List filters) { - for (Filter filter : filters) { - if (filter.isEnabledForInvocationType(InvocationType.CONSUMER)) { - consumerChains.addFilter(filter); - } + public FilterChainsManager setEdgeFilters(List filters) { + edgeChains = new InvocationFilterChains(filters); + return this; + } - if (filter.isEnabledForInvocationType(InvocationType.PRODUCER)) { - producerChains.addFilter(filter); - } - } + @Autowired + public FilterChainsManager setConsumerFilters(List filters) { + consumerChains = new InvocationFilterChains(filters); + return this; + } + @Autowired + public FilterChainsManager setProviderFilters(List filters) { + providerChains = new InvocationFilterChains(filters); return this; } @@ -49,29 +53,36 @@ public FilterChainsManager init() { return this; } - public FilterNode findConsumerChain(String microserviceName) { - return consumerChains.findChain(microserviceName); + public FilterNode findConsumerChain(String application, String serviceName) { + return consumerChains.findChain(application, serviceName); } - public FilterNode findProducerChain(String microserviceName) { - return producerChains.findChain(microserviceName); + public FilterNode findProducerChain(String application, String serviceName) { + return providerChains.findChain(application, serviceName); + } + + public FilterNode findEdgeChain(String application, String serviceName) { + return edgeChains.findChain(application, serviceName); } public String collectResolvedChains() { StringBuilder sb = new StringBuilder(); appendLine(sb, "consumer: "); - appendLine(sb, " filters: %s", collectFilterNames(consumerChains, InvocationType.CONSUMER)); + appendLine(sb, " filters: %s", collectFilterNames(consumerChains)); appendLine(sb, "producer: "); - appendLine(sb, " filters: %s", collectFilterNames(producerChains, InvocationType.PRODUCER)); + appendLine(sb, " filters: %s", collectFilterNames(providerChains)); + + appendLine(sb, "edge: "); + appendLine(sb, " filters: %s", collectFilterNames(edgeChains)); return deleteLast(sb, 1).toString(); } - private List collectFilterNames(InvocationFilterChains chains, InvocationType invocationType) { + private List collectFilterNames(InvocationFilterChains chains) { return chains.getFilters().stream() - .map(filter -> filter.getName() + "(" + filter.getOrder(invocationType, null) + ")") + .map(filter -> filter.getName() + "(" + filter.getOrder() + ")") .collect(Collectors.toList()); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java b/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java index fa9ec9d1a8e..44a5b34d901 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java @@ -62,19 +62,14 @@ private void setNextNode(FilterNode nextNode) { } public CompletableFuture onFilter(Invocation invocation) { - if (!filter.isEnabledForTransport(invocation.getTransportName())) { + // When transport name is empty, maybe edge transport filters need to be executed. + // Can't set Endpoint before load balance in edge. + if (invocation.getTransportName() != null && !filter.enabledForTransport(invocation.getTransportName())) { return nextNode.onFilter(invocation); } - return AsyncUtils.tryCatchSupplierFuture(() -> filter.onFilter(invocation, nextNode)) - .thenApply(this::rethrowExceptionInResponse); - } - - private Response rethrowExceptionInResponse(Response response) { - if (response.isFailed() && response.getResult() instanceof Throwable) { - throw AsyncUtils.rethrow(response.getResult()); - } - - return response; + String stage = invocation.getInvocationStageTrace().recordStageBegin(this.filter.getNameWithOrder()); + return AsyncUtils.tryCatchSupplierFuture(() -> filter.onFilter(invocation, nextNode) + .whenComplete((r, e) -> invocation.getInvocationStageTrace().recordStageEnd(stage))); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.java b/core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.java index c831b28d39a..6dde66c3354 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.java @@ -14,47 +14,38 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.servicecomb.core.filter; -import java.util.Collection; import java.util.Comparator; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; public class InvocationFilterChains { - private final Map filters = new HashMap<>(); - - private final Map microserviceChains = new HashMap<>(); - private final InvocationType invocationType; + private final List filters; - public InvocationFilterChains(InvocationType invocationType) { - this.invocationType = invocationType; - } + private final Map> microserviceChains = new ConcurrentHashMapEx<>(); - public Collection getFilters() { - return filters.values(); + public InvocationFilterChains(List filters) { + this.filters = filters; } - public void addFilter(Filter filter) { - filters.put(filter.getName(), filter); + public List getFilters() { + return filters; } - public FilterNode findChain(String microserviceName) { - FilterNode filterNode = microserviceChains.get(microserviceName); - if (filterNode == null) { - List serviceFilters = filters.entrySet().stream() - .filter(e -> e.getValue().isEnabledForMicroservice(microserviceName)) - .map(e -> e.getValue()) - .collect(Collectors.toList()); - serviceFilters.sort(Comparator.comparingInt(a -> a.getOrder(invocationType, microserviceName))); - filterNode = FilterNode.buildChain(serviceFilters); - microserviceChains.put(microserviceName, filterNode); - } - return filterNode; + public FilterNode findChain(String application, String serviceName) { + return microserviceChains.computeIfAbsent(application, key -> new ConcurrentHashMapEx<>()) + .computeIfAbsent(serviceName, (serviceNameInner) -> { + List serviceFilters = filters.stream() + .filter(e -> e.enabledForMicroservice(application, serviceName)) + .sorted(Comparator.comparingInt(a -> a.getOrder(application, serviceName))) + .collect(Collectors.toList()); + return FilterNode.buildChain(serviceFilters); + }); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/ProducerFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/ProducerFilter.java deleted file mode 100644 index 9d68af0d163..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/filter/ProducerFilter.java +++ /dev/null @@ -1,29 +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.servicecomb.core.filter; - -import javax.annotation.Nonnull; - -import org.apache.servicecomb.swagger.invocation.InvocationType; - -public interface ProducerFilter extends Filter { - @Nonnull - @Override - default boolean isEnabledForInvocationType(InvocationType invocationType) { - return invocationType == InvocationType.PRODUCER; - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/ProviderFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/ProviderFilter.java new file mode 100644 index 00000000000..6d5edd7814e --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/ProviderFilter.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.filter; + +public interface ProviderFilter extends Filter { + +} diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ContextMapperFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ContextMapperFilter.java new file mode 100644 index 00000000000..a14e41dd660 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ContextMapperFilter.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.filter.impl; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.core.governance.MatchType; +import org.apache.servicecomb.governance.handler.MapperHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.processor.mapping.Mapper; +import org.apache.servicecomb.swagger.invocation.Response; +import org.springframework.util.CollectionUtils; + +public class ContextMapperFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { + private final MapperHandler mapperHandler; + + public ContextMapperFilter(MapperHandler mapperHandler) { + this.mapperHandler = mapperHandler; + } + + @Override + public boolean enabledForTransport(String transport) { + return CoreConst.RESTFUL.equals(transport); + } + + @Override + public int getOrder() { + return -1995; + } + + @Override + public String getName() { + return "context-mapper"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); + Mapper mapper = mapperHandler.getActuator(request); + if (mapper == null || CollectionUtils.isEmpty(mapper.target())) { + return nextNode.onFilter(invocation); + } + Map properties = mapper.target(); + properties.forEach((k, v) -> { + if (StringUtils.isEmpty(v)) { + return; + } + if ("$U".equals(v)) { + invocation.addContext(k, request.apiPath()); + } else if ("$M".equals(v)) { + invocation.addContext(k, request.method()); + } else if (v.startsWith("$H{") && v.endsWith("}")) { + invocation.addContext(k, request.header(v.substring(3, v.length() - 1))); + } else if (v.startsWith("$Q{") && v.endsWith("}")) { + invocation.addContext(k, request.query(v.substring(3, v.length() - 1))); + } else { + invocation.addContext(k, v); + } + }); + return nextNode.onFilter(invocation); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.java index 2277b70f1ae..d2b8eb82319 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.java @@ -18,16 +18,16 @@ import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.swagger.invocation.Response; // just for test -public class EmptyFilter implements Filter { - @Nonnull +public class EmptyFilter extends AbstractFilter implements ProviderFilter, ConsumerFilter, EdgeFilter { @Override public String getName() { return "empty"; @@ -37,4 +37,9 @@ public String getName() { public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { return CompletableFuture.completedFuture(Response.ok(null)); } + + @Override + public int getOrder() { + return 0; + } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java index 880595d0415..b4bf065792f 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java @@ -17,24 +17,18 @@ package org.apache.servicecomb.core.filter.impl; import java.lang.reflect.Method; +import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; -import javax.validation.Validation; -import javax.validation.ValidatorFactory; -import javax.validation.executable.ExecutableValidator; -import javax.validation.groups.Default; - +import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.filter.ProducerFilter; +import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator; @@ -44,40 +38,91 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; -import com.netflix.config.DynamicPropertyFactory; +import jakarta.validation.Configuration; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Valid; +import jakarta.validation.Validation; +import jakarta.validation.ValidatorFactory; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.executable.ExecutableValidator; +import jakarta.validation.groups.Default; + +public class ParameterValidatorFilter extends AbstractFilter implements ProviderFilter, InitializingBean { + private static class Service { + @SuppressWarnings("unused") + public void service(@Valid Model model) { + + } + } + + private static class Model { + @NotNull + String name; + + @Min(10) + int age; + + Model(String name, int age) { + this.name = name; + this.age = age; + } + } -public class ParameterValidatorFilter implements ProducerFilter, InitializingBean { private static final Logger LOGGER = LoggerFactory.getLogger(ParameterValidatorFilter.class); public static final String NAME = "validator"; - private static final String ENABLE_EL = "servicecomb.filters.validation.useResourceBundleMessageInterpolator"; + public static final String HIBERNATE_VALIDATE_PREFIX = "hibernate.validator"; + + public static final String ENABLE_EL = "servicecomb.filters.validation.useResourceBundleMessageInterpolator"; protected ExecutableValidator validator; - @Nonnull @Override public String getName() { return NAME; } @Override - public int getOrder(InvocationType invocationType, String microservice) { - return Filter.PRODUCER_SCHEDULE_FILTER_ORDER + 1000; + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER + 1000; } @Override public void afterPropertiesSet() { validator = createValidatorFactory() .getValidator().forExecutables(); + initValidate(); + } + + private void initValidate() { + // This method is intended to make first rpc call faster + // Because validation cache bean class, this way only make first rpc call a little faster. + try { + Model model = new Model("foo", 23); + Service instance = new Service(); + Method method = Service.class.getMethod("service", Model.class); + Object[] args = new Object[] {model}; + validator.validateParameters(instance, method, args, Default.class); + } catch (Throwable e) { + throw new IllegalStateException(e); + } } protected ValidatorFactory createValidatorFactory() { - return Validation.byProvider(HibernateValidator.class) + Configuration validatorConfiguration = Validation.byProvider(HibernateValidator.class) .configure() .propertyNodeNameProvider(new JacksonPropertyNodeNameProvider()) - .messageInterpolator(messageInterpolator()) - .buildValidatorFactory(); + .messageInterpolator(messageInterpolator()); + Map properties = ConfigUtil.stringPropertiesWithPrefix(environment, HIBERNATE_VALIDATE_PREFIX); + if (!properties.isEmpty()) { + for (Map.Entry entry : properties.entrySet()) { + validatorConfiguration.addProperty(entry.getKey(), entry.getValue()); + } + } + return validatorConfiguration.buildValidatorFactory(); } protected AbstractMessageInterpolator messageInterpolator() { @@ -88,7 +133,7 @@ protected AbstractMessageInterpolator messageInterpolator() { } private boolean useResourceBundleMessageInterpolator() { - return DynamicPropertyFactory.getInstance().getBooleanProperty(ENABLE_EL, false).get(); + return environment.getProperty(ENABLE_EL, boolean.class, false); } @Override @@ -104,9 +149,7 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne protected Set> doValidate(Invocation invocation) { SwaggerProducerOperation producerOperation = invocation.getOperationMeta().getSwaggerProducerOperation(); - Object instance = producerOperation.getProducerInstance(); - Method method = producerOperation.getProducerMethod(); - Object[] args = invocation.toProducerArguments(); - return validator.validateParameters(instance, method, args, Default.class); + return validator.validateParameters(producerOperation.getProducerInstance(), producerOperation.getProducerMethod(), + invocation.toProducerArguments(), Default.class); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProducerOperationFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProducerOperationFilter.java deleted file mode 100644 index eb92e372dc3..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProducerOperationFilter.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.servicecomb.core.filter.impl; - -import java.lang.reflect.Method; -import java.util.concurrent.CompletableFuture; - -import javax.annotation.Nonnull; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.exception.Exceptions; -import org.apache.servicecomb.core.filter.Filter; -import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.filter.ProducerFilter; -import org.apache.servicecomb.foundation.common.utils.AsyncUtils; -import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; -import org.apache.servicecomb.swagger.invocation.InvocationType; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.context.ContextUtils; - -public class ProducerOperationFilter implements ProducerFilter { - public static final String NAME = "producer-operation"; - - @Nonnull - @Override - public String getName() { - return NAME; - } - - @Override - public int getOrder(InvocationType invocationType, String microservice) { - // almost time, should be the last filter. - return Filter.PRODUCER_SCHEDULE_FILTER_ORDER + 2000; - } - - @Override - public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - invocation.onBusinessMethodStart(); - - SwaggerProducerOperation producerOperation = invocation.getOperationMeta().getSwaggerProducerOperation(); - Object instance = producerOperation.getProducerInstance(); - Method method = producerOperation.getProducerMethod(); - Object[] args = invocation.toProducerArguments(); - return invoke(invocation, instance, method, args) - .thenApply(result -> convertResultToResponse(invocation, producerOperation, result)) - .whenComplete((response, throwable) -> processMetrics(invocation)); - } - - @SuppressWarnings("unchecked") - protected CompletableFuture invoke(Invocation invocation, Object instance, Method method, Object[] args) { - ContextUtils.setInvocationContext(invocation); - - try { - Object result = method.invoke(instance, args); - if (result instanceof CompletableFuture) { - return (CompletableFuture) result; - } - - return CompletableFuture.completedFuture(result); - } catch (Throwable e) { - return AsyncUtils.completeExceptionally(Exceptions.unwrap(e)); - } finally { - ContextUtils.removeInvocationContext(); - } - } - - protected Response convertResultToResponse(Invocation invocation, SwaggerProducerOperation producerOperation, - Object result) { - return producerOperation.getResponseMapper().mapResponse(invocation.getStatus(), result); - } - - protected void processMetrics(Invocation invocation) { - invocation.onBusinessMethodFinish(); - invocation.onBusinessFinish(); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java new file mode 100644 index 00000000000..c8f757bf3e2 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.filter.impl; + +import java.lang.reflect.Method; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.exception.Exceptions; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.foundation.common.utils.AsyncUtils; +import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +import jakarta.ws.rs.core.Response.Status; + +public class ProviderOperationFilter extends AbstractFilter implements ProviderFilter { + public static final String NAME = "producer-operation"; + + @Override + public String getName() { + return NAME; + } + + @Override + public int getOrder() { + // almost time, should be the last filter. + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER + 2000; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + if (!transportAccessAllowed(invocation)) { + return CompletableFuture.failedFuture(new InvocationException(Status.UNAUTHORIZED, + new CommonExceptionData("transport access not allowed."))); + } + invocation.onBusinessMethodStart(); + + SwaggerProducerOperation producerOperation = invocation.getOperationMeta().getSwaggerProducerOperation(); + Object instance = producerOperation.getProducerInstance(); + Method method = producerOperation.getProducerMethod(); + Object[] args = invocation.toProducerArguments(); + return invoke(invocation, instance, method, args) + .thenApply(result -> convertResultToResponse(invocation, producerOperation, result)) + .whenComplete((response, throwable) -> processMetrics(invocation)); + } + + private boolean transportAccessAllowed(Invocation invocation) { + if (invocation.getProviderTransportName() == null) { + return true; + } + return invocation.getProviderTransportName().equals(invocation.getTransportName()); + } + + @SuppressWarnings("unchecked") + protected CompletableFuture invoke(Invocation invocation, Object instance, Method method, Object[] args) { + ContextUtils.setInvocationContext(invocation); + + try { + Object result = method.invoke(instance, args); + if (result instanceof CompletableFuture) { + return (CompletableFuture) result; + } + + return CompletableFuture.completedFuture(result); + } catch (Throwable e) { + return AsyncUtils.completeExceptionally(Exceptions.unwrap(e)); + } finally { + ContextUtils.removeInvocationContext(); + } + } + + protected Response convertResultToResponse(Invocation invocation, SwaggerProducerOperation producerOperation, + Object result) { + return producerOperation.getResponseMapper().mapResponse(invocation.getStatus(), result); + } + + protected void processMetrics(Invocation invocation) { + invocation.onBusinessFinish(); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/RetryFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/RetryFilter.java new file mode 100644 index 00000000000..e423b8f7ebb --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/RetryFilter.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.servicecomb.core.filter.impl; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; +import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.governance.GovernanceConfiguration; +import org.apache.servicecomb.core.governance.MatchType; +import org.apache.servicecomb.core.governance.RetryContext; +import org.apache.servicecomb.governance.handler.RetryHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.swagger.invocation.Response; +import org.springframework.beans.factory.annotation.Autowired; + +import io.github.resilience4j.decorators.Decorators; +import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; +import io.github.resilience4j.retry.Retry; + +public class RetryFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { + private static final Object LOCK = new Object(); + + private static volatile ScheduledExecutorService reactiveRetryPool; + + private static ScheduledExecutorService getOrCreateRetryPool() { + if (reactiveRetryPool == null) { + synchronized (LOCK) { + if (reactiveRetryPool == null) { + reactiveRetryPool = Executors.newScheduledThreadPool(2, new ThreadFactory() { + private final AtomicInteger count = new AtomicInteger(0); + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "reactive-retry-pool-thread-" + count.getAndIncrement()); + // avoid block shutdown + thread.setDaemon(true); + return thread; + } + }); + } + } + } + return reactiveRetryPool; + } + + private final RetryHandler retryHandler; + + @Autowired + public RetryFilter(RetryHandler retryHandler) { + this.retryHandler = retryHandler; + } + + @Override + public String getName() { + return "retry"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); + Retry retry = retryHandler.getActuator(request); + if (retry == null) { + return nextNode.onFilter(invocation); + } + + Supplier> next = createBusinessCompletionStageSupplier(invocation, nextNode); + DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); + dcs.withRetry(retry, getOrCreateRetryPool()); + CompletableFuture future = new CompletableFuture<>(); + dcs.get().whenComplete((r, e) -> { + if (e == null) { + future.complete(r); + return; + } + + future.completeExceptionally(e); + }); + + return future; + } + + private Supplier> createBusinessCompletionStageSupplier(Invocation invocation, + FilterNode nextNode) { + return () -> { + updateRetryStatus(invocation); + return nextNode.onFilter(invocation); + }; + } + + private static void updateRetryStatus(Invocation invocation) { + if (invocation.getLocalContext(RetryContext.RETRY_CONTEXT) != null) { + if (invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE) != null + && (boolean) invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE)) { + // clear last server to avoid using user defined endpoint + invocation.setEndpoint(null); + } + RetryContext retryContext = invocation.getLocalContext(RetryContext.RETRY_CONTEXT); + retryContext.incrementRetry(); + return; + } + + invocation.addLocalContext(RetryContext.RETRY_CONTEXT, + new RetryContext(GovernanceConfiguration.getRetrySameServer(invocation.getMicroserviceName()))); + } + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1990; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ScheduleFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ScheduleFilter.java index 00d430a7b02..341c01fa877 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ScheduleFilter.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ScheduleFilter.java @@ -19,67 +19,52 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.exception.Exceptions; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.filter.ProducerFilter; -import org.apache.servicecomb.core.invocation.InvocationStageTrace; -import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.core.tracing.TraceIdLogger; import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.slf4j.MDC; + +import jakarta.ws.rs.core.Response.Status; -public class ScheduleFilter implements ProducerFilter { +public class ScheduleFilter extends AbstractFilter implements ProviderFilter { public static final String NAME = "schedule"; - @Nonnull @Override public String getName() { return NAME; } @Override - public int getOrder(InvocationType invocationType, String microservice) { - return Filter.PRODUCER_SCHEDULE_FILTER_ORDER; + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode next) { - invocation.getInvocationStageTrace().startSchedule(); + invocation.getInvocationStageTrace().startProviderQueue(); Executor executor = invocation.getOperationMeta().getExecutor(); return CompletableFuture.completedFuture(null) .thenComposeAsync(response -> runInExecutor(invocation, next), executor); } protected CompletableFuture runInExecutor(Invocation invocation, FilterNode next) { - invocation.onExecuteStart(); - - try { - InvocationStageTrace trace = invocation.getInvocationStageTrace(); - trace.startServerFiltersRequest(); - invocation.onStartHandlersRequest(); - - checkInQueueTimeout(invocation); - - return next.onFilter(invocation) - .whenComplete((response, throwable) -> whenComplete(invocation)); - } finally { - invocation.onExecuteFinish(); - } + invocation.getInvocationStageTrace().finishProviderQueue(); + MDC.put(TraceIdLogger.KEY_TRACE_ID, invocation.getTraceId()); + checkInQueueTimeout(invocation); + return next.onFilter(invocation); } private void checkInQueueTimeout(Invocation invocation) { long nanoTimeout = invocation.getOperationMeta().getConfig() .getNanoRequestWaitInPoolTimeout(invocation.getTransport().getName()); - if (System.nanoTime() - invocation.getInvocationStageTrace().getStart() > nanoTimeout) { - throw Exceptions.genericProducer("Request in the queue timed out."); + if (invocation.getInvocationStageTrace().calcQueue() > nanoTimeout) { + throw new InvocationException(Status.REQUEST_TIMEOUT, "Request in the queue timed out."); } } - - private void whenComplete(Invocation invocation) { - invocation.getInvocationStageTrace().finishHandlersResponse(); - invocation.getInvocationStageTrace().finishServerFiltersResponse(); - } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/SimpleLoadBalanceFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/SimpleLoadBalanceFilter.java deleted file mode 100644 index b70a87cb4cb..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/filter/impl/SimpleLoadBalanceFilter.java +++ /dev/null @@ -1,176 +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.servicecomb.core.filter.impl; - -import static org.apache.servicecomb.core.exception.ExceptionCodes.LB_ADDRESS_NOT_FOUND; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.annotation.Nonnull; - -import org.apache.servicecomb.core.Endpoint; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.exception.Exceptions; -import org.apache.servicecomb.core.filter.ConsumerFilter; -import org.apache.servicecomb.core.filter.Filter; -import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.governance.RetryContext; -import org.apache.servicecomb.core.registry.discovery.EndpointDiscoveryFilter; -import org.apache.servicecomb.foundation.common.cache.VersionedCache; -import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; -import org.apache.servicecomb.registry.discovery.DiscoveryContext; -import org.apache.servicecomb.registry.discovery.DiscoveryFilter; -import org.apache.servicecomb.registry.discovery.DiscoveryTree; -import org.apache.servicecomb.swagger.invocation.InvocationType; -import org.apache.servicecomb.swagger.invocation.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * build-in round robin LB, for demo scenes - */ -public class SimpleLoadBalanceFilter implements ConsumerFilter { - private static final Logger LOGGER = LoggerFactory.getLogger(SimpleLoadBalanceFilter.class); - - public static final String NAME = "simple-load-balance"; - - private static class Service { - public static final String CONTEXT_KEY_LAST_SERVER = "x-context-last-server"; - - // Enough times to make sure to choose a different server in high volume. - private static final int COUNT = 17; - - private final String name; - - private final DiscoveryTree discoveryTree = new DiscoveryTree(); - - // key is grouping filter qualified name - private final Map indexMap = new ConcurrentHashMapEx<>(); - - public Service(String name) { - this.name = name; - discoveryTree.loadFromSPI(DiscoveryFilter.class); - discoveryTree.addFilter(new EndpointDiscoveryFilter()); - discoveryTree.sort(); - } - - public String getName() { - return name; - } - - public Endpoint selectEndpoint(Invocation invocation) { - DiscoveryContext context = new DiscoveryContext(); - context.setInputParameters(invocation); - VersionedCache endpointsVersionedCache = discoveryTree.discovery(context, - invocation.getAppId(), - invocation.getMicroserviceName(), - invocation.getMicroserviceVersionRule()); - if (endpointsVersionedCache.isEmpty()) { - String msg = "No available address found."; - LOGGER.error("{} microserviceName={}, version={}, discoveryGroupName={}", - msg, - invocation.getMicroserviceName(), - invocation.getMicroserviceVersionRule(), - endpointsVersionedCache.name()); - throw Exceptions.consumer(LB_ADDRESS_NOT_FOUND, msg); - } - - return selectEndpoint(invocation, endpointsVersionedCache.name(), endpointsVersionedCache.data()); - } - - private Endpoint selectEndpoint(Invocation invocation, String key, List endpoints) { - RetryContext retryContext = invocation.getLocalContext(RetryContext.RETRY_CONTEXT); - if (retryContext == null) { - return chooseEndpoint(invocation, key, endpoints); - } - - if (!retryContext.isRetry()) { - Endpoint server = chooseEndpoint(invocation, key, endpoints); - invocation.addLocalContext(CONTEXT_KEY_LAST_SERVER, server); - return server; - } - - Endpoint lastServer = invocation.getLocalContext(CONTEXT_KEY_LAST_SERVER); - Endpoint nextServer = lastServer; - if (!retryContext.trySameServer()) { - for (int i = 0; i < COUNT; i++) { - Endpoint s = chooseEndpoint(invocation, key, endpoints); - if (s == null) { - break; - } - if (!s.equals(nextServer)) { - nextServer = s; - break; - } - } - } - - LOGGER.info("operation failed {}, retry to instance [{}], last instance [{}], trace id {}", - invocation.getMicroserviceQualifiedName(), - nextServer == null ? "" : nextServer.getEndpoint(), - lastServer == null ? "" : lastServer.getEndpoint(), - invocation.getTraceId()); - invocation.addLocalContext(CONTEXT_KEY_LAST_SERVER, nextServer); - return nextServer; - } - - private Endpoint chooseEndpoint(Invocation invocation, String key, List endpoints) { - AtomicInteger index = indexMap.computeIfAbsent(key, name -> { - LOGGER.info("Create loadBalancer for {}.", name); - return new AtomicInteger(); - }); - LOGGER.debug("invocation {} use discoveryGroup {}.", - invocation.getMicroserviceQualifiedName(), - key); - - int idx = Math.abs(index.getAndIncrement()); - idx = idx % endpoints.size(); - - return endpoints.get(idx); - } - } - - - private final Map servicesByName = new ConcurrentHashMapEx<>(); - - @Nonnull - @Override - public String getName() { - return NAME; - } - - @Override - public int getOrder(InvocationType invocationType, String microservice) { - return Filter.CONSUMER_LOAD_BALANCE_ORDER; - } - - @Override - public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - if (invocation.getEndpoint() != null) { - invocation.addLocalContext(RetryContext.RETRY_LOAD_BALANCE, false); - return nextNode.onFilter(invocation); - } - invocation.addLocalContext(RetryContext.RETRY_LOAD_BALANCE, true); - Service service = servicesByName.computeIfAbsent(invocation.getMicroserviceName(), Service::new); - Endpoint endpoint = service.selectEndpoint(invocation); - invocation.setEndpoint(endpoint); - return nextNode.onFilter(invocation); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/CoreGovernanceConfiguration.java b/core/src/main/java/org/apache/servicecomb/core/governance/CoreGovernanceConfiguration.java index 957eef55f27..948c12bb46a 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/CoreGovernanceConfiguration.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/CoreGovernanceConfiguration.java @@ -16,6 +16,7 @@ */ package org.apache.servicecomb.core.governance; +import org.apache.servicecomb.core.SCBEngine; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -32,8 +33,8 @@ public ServiceCombInstanceIsolationExtension serviceCombInstanceIsolationExtensi } @Bean - public ServiceCombMicroserviceMeta serviceCombMicroserviceMeta() { - return new ServiceCombMicroserviceMeta(); + public ServiceCombMicroserviceMeta serviceCombMicroserviceMeta(SCBEngine scbEngine) { + return new ServiceCombMicroserviceMeta(scbEngine); } @Bean diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/GovernanceConfiguration.java b/core/src/main/java/org/apache/servicecomb/core/governance/GovernanceConfiguration.java index eff944fff82..e0e65b9eec8 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/GovernanceConfiguration.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/GovernanceConfiguration.java @@ -16,7 +16,7 @@ */ package org.apache.servicecomb.core.governance; -import com.netflix.config.DynamicPropertyFactory; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; public class GovernanceConfiguration { public static final String ROOT = "servicecomb.loadbalance."; @@ -62,7 +62,7 @@ private static int getRetryServer(String microservice, String retryType) { public static String getStringProperty(String defaultValue, String... keys) { String property; for (String key : keys) { - property = DynamicPropertyFactory.getInstance().getStringProperty(key, null).get(); + property = LegacyPropertyFactory.getStringProperty(key); if (property != null) { return property; } diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java b/core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java index 935f9384a4b..15d58d66610 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java @@ -20,7 +20,9 @@ import java.util.Map; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.swagger.SwaggerUtils; public final class MatchType { private static class GovernanceRequestExtractorImpl implements GovernanceRequestExtractor { @@ -33,8 +35,8 @@ private GovernanceRequestExtractorImpl(Invocation invocation) { @Override public String apiPath() { if (MatchType.REST.equalsIgnoreCase(invocation.getOperationMeta().getConfig().getGovernanceMatchType())) { - if (invocation.isConsumer()) { - return concatAbsolutePath(invocation.getSchemaMeta().getSwagger().getBasePath(), + if (!invocation.isProducer()) { + return SwaggerUtils.concatAbsolutePath(invocation.getSchemaMeta().getSwagger(), invocation.getOperationMeta().getOperationPath()); } // not highway @@ -43,7 +45,7 @@ public String apiPath() { } } - if (invocation.isConsumer()) { + if (!invocation.isProducer()) { return invocation.getOperationMeta().getMicroserviceQualifiedName(); } return invocation.getOperationMeta().getSchemaQualifiedName(); @@ -72,9 +74,18 @@ public String header(String key) { return null; } + @Override + public String query(String key) { + HttpServletRequestEx requestEx = invocation.getRequestEx(); + if (requestEx == null) { + return null; + } + return requestEx.getParameter(key); + } + @Override public String instanceId() { - if (invocation.isConsumer()) { + if (!invocation.isProducer()) { if (invocation.getEndpoint() != null && invocation.getEndpoint().getMicroserviceInstance() != null) { return invocation.getEndpoint().getMicroserviceInstance().getInstanceId(); } @@ -84,7 +95,7 @@ public String instanceId() { @Override public String serviceName() { - if (invocation.isConsumer()) { + if (!invocation.isProducer()) { return invocation.getMicroserviceName(); } return null; @@ -103,19 +114,4 @@ public Object sourceRequest() { public static GovernanceRequestExtractor createGovHttpRequest(Invocation invocation) { return new GovernanceRequestExtractorImpl(invocation); } - - /** - * Concat the two paths to an absolute path, without end of '/'. - * - * e.g. "/" + "/ope" = /ope - * e.g. "/prefix" + "/ope" = /prefix/ope - */ - private static String concatAbsolutePath(String basePath, String operationPath) { - return ("/" + nonNullify(basePath) + "/" + nonNullify(operationPath)) - .replaceAll("/{2,}", "/"); - } - - private static String nonNullify(String path) { - return path == null ? "" : path; - } } diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.java b/core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.java index 058982a425a..47792792e14 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.java @@ -19,6 +19,7 @@ public class RetryContext { public static final String RETRY_CONTEXT = "x-context-retry"; + // weather need reset Endpoint in retry public static final String RETRY_LOAD_BALANCE = "x-context-retry-loadbalance"; private boolean retry; diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java index 77f36c5a3d7..9177ca71820 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java @@ -17,11 +17,10 @@ package org.apache.servicecomb.core.governance; -import javax.ws.rs.core.Response.Status; +import java.util.List; import org.apache.servicecomb.governance.handler.ext.AbstractCircuitBreakerExtension; import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; public class ServiceCombCircuitBreakerExtension extends AbstractCircuitBreakerExtension { @@ -41,15 +40,11 @@ protected String extractStatusCode(Object result) { } @Override - public boolean isFailedResult(Throwable e) { + public boolean isFailedResult(List statusList, Throwable e) { if (e instanceof InvocationException) { InvocationException invocationException = (InvocationException) e; - if (invocationException.getStatusCode() == Status.SERVICE_UNAVAILABLE.getStatusCode() || - invocationException.getStatusCode() == Status.BAD_GATEWAY.getStatusCode() || - invocationException.getStatusCode() == ExceptionFactory.PRODUCER_INNER_STATUS_CODE) { - return true; - } + return statusList.contains(String.valueOf(invocationException.getStatusCode())); } - return super.isFailedResult(e); + return super.isFailedResult(statusList, e); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombConfigurationEventAdapter.java b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombConfigurationEventAdapter.java index bb15d232f61..e5ee61cd586 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombConfigurationEventAdapter.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombConfigurationEventAdapter.java @@ -18,11 +18,7 @@ package org.apache.servicecomb.core.governance; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.apache.servicecomb.config.event.RefreshGovernanceConfigurationEvent; +import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; @@ -35,19 +31,8 @@ public ServiceCombConfigurationEventAdapter() { } @Subscribe - public void onConfigurationChangedEvent(RefreshGovernanceConfigurationEvent event) { - Set changedKeys = new HashSet<>(); - addMap(changedKeys, event.getEvent().getAdded()); - addMap(changedKeys, event.getEvent().getDeleted()); - addMap(changedKeys, event.getEvent().getChanged()); - addMap(changedKeys, event.getEvent().getComplete()); - GovernanceConfigurationChangedEvent newEvent = new GovernanceConfigurationChangedEvent(changedKeys); + public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { + GovernanceConfigurationChangedEvent newEvent = new GovernanceConfigurationChangedEvent(event.getChanged()); GovernanceEventManager.post(newEvent); } - - private void addMap(Set keys, Map changed) { - if (changed != null) { - keys.addAll(changed.keySet()); - } - } } diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java index f3b9075eaef..2bbf6d3c87d 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java @@ -17,10 +17,12 @@ package org.apache.servicecomb.core.governance; +import java.util.List; + import org.apache.servicecomb.governance.handler.ext.AbstractInstanceIsolationExtension; public class ServiceCombInstanceIsolationExtension extends AbstractInstanceIsolationExtension { - private ServiceCombRetryExtension retryExtension = new ServiceCombRetryExtension(); + private final ServiceCombRetryExtension retryExtension = new ServiceCombRetryExtension(); @Override protected String extractStatusCode(Object result) { @@ -28,7 +30,7 @@ protected String extractStatusCode(Object result) { } @Override - public boolean isFailedResult(Throwable e) { - return retryExtension.isFailedResult(e); + public boolean isFailedResult(List statusList, Throwable e) { + return retryExtension.isFailedResult(statusList, e); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombMicroserviceMeta.java b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombMicroserviceMeta.java index af95fe9ad32..3893e3e4207 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombMicroserviceMeta.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombMicroserviceMeta.java @@ -17,17 +17,24 @@ package org.apache.servicecomb.core.governance; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.governance.MicroserviceMeta; -import org.apache.servicecomb.registry.RegistrationManager; public class ServiceCombMicroserviceMeta implements MicroserviceMeta { + private SCBEngine scbEngine; + + public ServiceCombMicroserviceMeta(SCBEngine scbEngine) { + this.scbEngine = scbEngine; + } + @Override public String getName() { - return RegistrationManager.INSTANCE.getMicroservice().getServiceName(); + return BootStrapProperties.readServiceName(scbEngine.getEnvironment()); } @Override public String getVersion() { - return RegistrationManager.INSTANCE.getMicroservice().getVersion(); + return BootStrapProperties.readServiceVersion(scbEngine.getEnvironment()); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java index ae312b288c9..871a03bfa30 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java @@ -17,11 +17,10 @@ package org.apache.servicecomb.core.governance; -import javax.ws.rs.core.Response.Status; +import java.util.List; import org.apache.servicecomb.governance.handler.ext.AbstractRetryExtension; import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; public class ServiceCombRetryExtension extends AbstractRetryExtension { @@ -41,15 +40,11 @@ protected String extractStatusCode(Object result) { } @Override - public boolean isFailedResult(Throwable e) { + public boolean isFailedResult(List statusList, Throwable e) { if (e instanceof InvocationException) { InvocationException invocationException = (InvocationException) e; - if (invocationException.getStatusCode() == Status.SERVICE_UNAVAILABLE.getStatusCode() || - invocationException.getStatusCode() == Status.BAD_GATEWAY.getStatusCode() || - invocationException.getStatusCode() == ExceptionFactory.CONSUMER_INNER_STATUS_CODE) { - return true; - } + return statusList.contains(String.valueOf(invocationException.getStatusCode())); } - return super.isFailedResult(e); + return super.isFailedResult(statusList, e); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/CoreInvocationConfiguration.java b/core/src/main/java/org/apache/servicecomb/core/invocation/CoreInvocationConfiguration.java index 8fadf19d2b0..abfe2f05330 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/CoreInvocationConfiguration.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/CoreInvocationConfiguration.java @@ -29,19 +29,19 @@ @Configuration public class CoreInvocationConfiguration { @Bean - public InvocationTimeoutBootListener invocationTimeoutBootListener(EventBus eventBus, + public InvocationTimeoutBootListener scbInvocationTimeoutBootListener(EventBus eventBus, List strategies, Environment environment) { return new InvocationTimeoutBootListener(eventBus, strategies, environment); } @Bean - public PassingTimeStrategy passingTimeStrategy() { + public PassingTimeStrategy scbPassingTimeStrategy() { return new PassingTimeStrategy(); } @Bean - public ProcessingTimeStrategy processingTimeStrategy() { + public ProcessingTimeStrategy scbProcessingTimeStrategy() { return new ProcessingTimeStrategy(); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java index a413fce1016..323ad924a09 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java @@ -20,9 +20,6 @@ import org.apache.servicecomb.core.Invocation; -/** - * better to named InvocationFactory, but already be used by old version - */ public interface InvocationCreator { CompletableFuture createAsync(); } diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationFactory.java b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationFactory.java index f3504b03409..776fdb32568 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationFactory.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationFactory.java @@ -19,17 +19,14 @@ import java.util.Map; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; -import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.registry.api.registry.Microservice; - -import com.netflix.config.DynamicPropertyFactory; public final class InvocationFactory { private InvocationFactory() { @@ -45,25 +42,28 @@ public static Invocation forConsumer(ReferenceConfig referenceConfig, OperationM } public static Invocation setSrcMicroservice(Invocation invocation) { - Microservice microservice = RegistrationManager.INSTANCE.getMicroservice(); - invocation.addContext(Const.SRC_MICROSERVICE, microservice.getServiceName()); + invocation.addContext(CoreConst.SRC_MICROSERVICE, + BootStrapProperties.readServiceName(SCBEngine.getInstance().getEnvironment())); + // TODO: hard code registry name here. This is an old feature not for all registry implementations. if (addSourceServiceId()) { - invocation.addContext(Const.SRC_SERVICE_ID, microservice.getServiceId()); + invocation.addContext(CoreConst.SRC_SERVICE_ID, + SCBEngine.getInstance().getRegistrationManager().getServiceId("sc-registration")); } if (addSourceInstanceId()) { - invocation.addContext(Const.SRC_INSTANCE_ID, microservice.getInstance().getInstanceId()); + invocation.addContext(CoreConst.SRC_INSTANCE_ID, + SCBEngine.getInstance().getRegistrationManager().getInstanceId("sc-registration")); } return invocation; } public static boolean addSourceServiceId() { - return DynamicPropertyFactory.getInstance(). - getBooleanProperty("servicecomb.context.source.serviceId", true).get(); + return SCBEngine.getInstance().getEnvironment(). + getProperty("servicecomb.context.source.serviceId", boolean.class, true); } public static boolean addSourceInstanceId() { - return DynamicPropertyFactory.getInstance(). - getBooleanProperty("servicecomb.context.source.instanceId", true).get(); + return SCBEngine.getInstance().getEnvironment(). + getProperty("servicecomb.context.source.instanceId", boolean.class, true); } /* diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationStageTrace.java b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationStageTrace.java index 3f06bb8f01a..f9d0166a458 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationStageTrace.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationStageTrace.java @@ -16,362 +16,336 @@ */ package org.apache.servicecomb.core.invocation; +import java.util.HashMap; +import java.util.Map; + import org.apache.servicecomb.core.Invocation; /** *
- * important:
- *   all time point is about invocation stage, not java method
- *   all time is nanoTime
- *   not all stage relate to a event, currently, we only have 4 event:
- *     start/finish/startBusiness/finishBusiness
- *
  * for consumer:
- *         (prepare)                      (handlerReq)                                       (clientFilterReq)
- *   start --------> startHandlersRequest -----------> startClientFiltersRequest --------------------------------------
- *                                                        <----------------(sendRequest)----------------->            |
- *                        (receiveResponse)              (writeToBuffer)                   (getConnection)            |
- *    ---finishReceiveResponse <----- finishWriteToBuffer <----------- finishGetConnection <-------------- startSend <-
- *   | (wakeConsumer)            (clientFiltersResponse)               (handlersResponse)
- *   |-----> startClientFiltersResponse -------> finishClientFiltersResponse -------> finishHandlersResponse --> finish
  *
- * for producer:
- *       (prepare)       (threadPoolQueue)                                (serverFiltersRequest)
- *   start ----> startSchedule -----> startExecution -> startServerFiltersRequest -------> startHandlersRequest -------
- *                          (handlersResponse)          <-------------(business)------------->      (handlersRequest) |
- *   -----finishHandlersResponse <------ finishBusiness <------- finishBusinessMethod <------ startBusinessMethod------
- *   | (serverFiltersResponse)     (sendResponse)
- *   |---> finishServerFiltersResponse ------> finish
+ *  1. total: start create invocation -> all filters finished
+ *  2. prepare: start create invocation -> finish create invocation
+ *  3. filters-(filter-name): start call on filter -> on complete
+ *  4. connection: start get connection -> finish get connection
+ *  5. consumer-encode: start encode request -> finish encode request
+ *  6. consumer-decode: start decode response -> finish decode response
+ *  7. consumer-send: start send request -> finish send request
+ *  8. wait: finish send request -> start decode response
+ *
+ * for provider:
+ *  1. total: start create invocation -> all filters finished
+ *  2. prepare: start create invocation -> finish create invocation
+ *  3. filters-(filter-name): start call on filter -> on complete
+ *  4. queue: add in queue -> execute in thread
+ *  5. provider-decode: start decode request -> finish decode request
+ *  6. provider-encode: start encode response -> finish encode response
+ *  7. provider-send: start send response -> finish send response
+ *  8. execute: start business execute -> finish business execute
  *
  * for edge:
- *      (prepare)         (threadPoolQueue)                               (serverFiltersRequest)
- *   start ----> startSchedule -----> startExecution -> startServerFiltersRequest ----> startHandlersRequest ----------
- *                           <----------(sendRequest)---------->                                                      |
- *                       (writeToBuffer)              (getConnection)  (clientFilterReq)            (handlersRequest) |
- *   --- finishWriteToBuffer <------ finishGetConnection <------ startSend <------ startClientFiltersRequest <---------
- *   | (receiveResponse)     (wakeConsumer)                 (clientFiltersResponse)
- *   ---> finishReceiveResponse ------> startClientFiltersResponse ------> finishClientFiltersResponse ----------------
- *                                   (sendResponse)                  (serverFiltersResponse)       (handlersResponse) |
- *                              finish <------ finishServerFiltersResponse <------ finishHandlersResponse <------------
+ *
+ *  *  1. total: start create invocation -> all filters finished
+ *  *  2. prepare: start create invocation -> finish create invocation
+ *  *  3. filters-(filter-name): start call on filter -> on complete
+ *  *  4. connection: start get connection -> finish get connection
+ *  *  5. provider-decode: start decode request -> finish decode request
+ *  *  6. provider-encode: start encode response -> finish encode response
+ *  *  7. consumer-encode: start encode request -> finish encode request
+ *  *  8. consumer-decode: start decode response -> finish decode response
+ *  *  9. consumer-send: start send request -> finish send request
+ *  *  10. provider-send: start send response -> finish send response
+ *  *  11. wait: finish send request -> start decode response
  *
  * 
*/ public class InvocationStageTrace { - public static final String PREPARE = "prepare"; + public static class Stage { + private long beginTime; + + private long endTime; + + public long getBeginTime() { + return beginTime; + } - public static final String HANDLERS_REQUEST = "handlers request"; + public long getEndTime() { + return endTime; + } + } - public static final String HANDLERS_RESPONSE = "handlers response"; + public static final String STAGE_TOTAL = "total"; - public static final String CLIENT_FILTERS_REQUEST = "client filters request"; + public static final String STAGE_PREPARE = "prepare"; - public static final String CONSUMER_SEND_REQUEST = "send request"; + public static final String STAGE_PROVIDER_QUEUE = "queue"; - public static final String CONSUMER_GET_CONNECTION = "get connection"; + public static final String STAGE_PROVIDER_DECODE_REQUEST = "provider-decode"; - public static final String CONSUMER_WRITE_TO_BUF = "write to buf"; + public static final String STAGE_PROVIDER_ENCODE_RESPONSE = "provider-encode"; - public static final String CONSUMER_WAIT_RESPONSE = "wait response"; + public static final String STAGE_PROVIDER_SEND = "provider-send"; - public static final String CONSUMER_WAKE_CONSUMER = "wake consumer"; + public static final String STAGE_PROVIDER_BUSINESS = "execute"; - public static final String CLIENT_FILTERS_RESPONSE = "client filters response"; + public static final String STAGE_CONSUMER_CONNECTION = "connection"; - public static final String THREAD_POOL_QUEUE = "threadPoolQueue"; + public static final String STAGE_CONSUMER_ENCODE_REQUEST = "consumer-encode"; - public static final String SERVER_FILTERS_REQUEST = "server filters request"; + public static final String STAGE_CONSUMER_DECODE_RESPONSE = "consumer-decode"; - public static final String SERVER_FILTERS_RESPONSE = "server filters response"; + public static final String STAGE_CONSUMER_SEND = "consumer-send"; - public static final String PRODUCER_SEND_RESPONSE = "send response"; + public static final String STAGE_CONSUMER_WAIT = "wait"; private final Invocation invocation; - // current time for start invocation - private long startTimeMillis; + // invocation start time in millis, for passing strategy use only + private long startInMillis; + // invocation start time in nanos, for passing strategy use only private long start; - private long startHandlersRequest; + private long finish; - private long startClientFiltersRequest; + private long startCreateInvocation; - // only for consumer - private long startSend; + private long finishCreateInvocation; - // only for consumer - private long startGetConnection; + private long startProviderQueue; - // only for consumer - private long finishGetConnection; + private long finishProviderQueue; - // only for consumer - private long finishWriteToBuffer; + private long startConsumerConnection; - // only for consumer - private long finishReceiveResponse; + private long finishConsumerConnection; - private long startClientFiltersResponse; + private long startProviderDecodeRequest; - private long finishClientFiltersResponse; + private long finishProviderDecodeRequest; - private long finishHandlersResponse; + private long startProviderEncodeResponse; - private long finish; + private long finishProviderEncodeResponse; - // only for producer: put producer task to thread pool - private long startSchedule; + private long startConsumerEncodeRequest; - private long startServerFiltersRequest; + private long finishConsumerEncodeRequest; - private long finishServerFiltersResponse; + private long startConsumerDecodeResponse; - // only for producer: start execute in work thread - // for reactive mode, work thread is eventloop - private long startExecution; + private long finishConsumerDecodeResponse; - // only for producer - private long startBusinessMethod; + private long startProviderSendResponse; - // only for producer - private long finishBusiness; + private long finishProviderSendResponse; - public InvocationStageTrace(Invocation invocation) { - this.invocation = invocation; - } + private long startConsumerSendRequest; - public void start(long start) { - // remember the current time to start invocation - this.startTimeMillis = System.currentTimeMillis(); - this.start = start; - } + private long finishConsumerSendRequest; - public long getStart() { - return start; - } + private long startBusinessExecute; - public long getStartTimeMillis() { - return startTimeMillis; - } + private long finishBusinessExecute; - public InvocationStageTrace setStartTimeMillis(long startTimeMillis) { - this.startTimeMillis = startTimeMillis; - return this; - } + private long startWaitResponse; - public long getStartHandlersRequest() { - return startHandlersRequest; - } + private long finishWaitResponse; - public void startHandlersRequest() { - this.startHandlersRequest = System.nanoTime(); - } - - public long getStartClientFiltersRequest() { - return startClientFiltersRequest; - } + // invocation stage can not be used in concurrent access + private final Map stages = new HashMap<>(); - public void startClientFiltersRequest() { - this.startClientFiltersRequest = System.nanoTime(); - } - - public long getStartSchedule() { - return startSchedule; - } - - public void startSchedule() { - this.startSchedule = System.nanoTime(); + public InvocationStageTrace(Invocation invocation) { + this.invocation = invocation; } - public long getStartExecution() { - return startExecution; + public String recordStageBegin(String stageName) { + String realStageName = stageName; + while (stages.get(realStageName) != null) { + realStageName = realStageName + "@"; + } + Stage stage = new Stage(); + stage.beginTime = System.nanoTime(); + stages.put(realStageName, stage); + return realStageName; } - public void startExecution() { - this.startExecution = System.nanoTime(); + public void recordStageEnd(String realStageName) { + Stage stage = stages.get(realStageName); + stage.endTime = nanoTime(); } - public long getStartSend() { - return startSend; + public Map getStages() { + return stages; } - public void startSend() { - this.startSend = System.nanoTime(); + public void finish() { + this.finish = nanoTime(); } - public long getFinishGetConnection() { - return finishGetConnection; + public void startCreateInvocation(long nano) { + this.startCreateInvocation = nano; + this.startInMillis = millisTime(); + this.start = nanoTime(); } - public void startGetConnection() { - this.startGetConnection = System.nanoTime(); + public void finishCreateInvocation() { + this.finishCreateInvocation = nanoTime(); } - public void finishGetConnection() { - this.finishGetConnection = System.nanoTime(); + public long calcPrepare() { + return calc(finishCreateInvocation, startCreateInvocation); } - public long getFinishWriteToBuffer() { - return finishWriteToBuffer; + public void startProviderQueue() { + this.startProviderQueue = nanoTime(); } - public void finishWriteToBuffer(long finishWriteToBuffer) { - this.finishWriteToBuffer = finishWriteToBuffer; + public void finishProviderQueue() { + this.finishProviderQueue = nanoTime(); } - public long getFinishReceiveResponse() { - return finishReceiveResponse; + public long calcQueue() { + return calc(finishProviderQueue, startProviderQueue); } - public void finishReceiveResponse() { - this.finishReceiveResponse = System.nanoTime(); + public void startProviderDecodeRequest() { + this.startProviderDecodeRequest = nanoTime(); } - public long getStartClientFiltersResponse() { - return startClientFiltersResponse; + public void finishProviderDecodeRequest() { + this.finishProviderDecodeRequest = nanoTime(); } - public void startClientFiltersResponse() { - this.startClientFiltersResponse = System.nanoTime(); + public long calcProviderDecodeRequest() { + return calc(finishProviderDecodeRequest, startProviderDecodeRequest); } - public long getFinishClientFiltersResponse() { - return finishClientFiltersResponse; + public void startProviderEncodeResponse() { + this.startProviderEncodeResponse = nanoTime(); } - public void finishClientFiltersResponse() { - this.finishClientFiltersResponse = System.nanoTime(); + public void finishProviderEncodeResponse() { + this.finishProviderEncodeResponse = nanoTime(); } - public long getFinishHandlersResponse() { - return finishHandlersResponse; + public long calcProviderEncodeResponse() { + return calc(finishProviderEncodeResponse, startProviderEncodeResponse); } - public void finishHandlersResponse() { - this.finishHandlersResponse = System.nanoTime(); + public void startConsumerEncodeRequest() { + this.startConsumerEncodeRequest = nanoTime(); } - public long getStartServerFiltersRequest() { - return startServerFiltersRequest; + public void finishConsumerEncodeRequest() { + this.finishConsumerEncodeRequest = nanoTime(); } - public void startServerFiltersRequest() { - this.startServerFiltersRequest = System.nanoTime(); + public long calcConsumerEncodeRequest() { + return calc(finishConsumerEncodeRequest, startConsumerEncodeRequest); } - public long getFinishServerFiltersResponse() { - return finishServerFiltersResponse; + public void startConsumerDecodeResponse() { + this.startConsumerDecodeResponse = nanoTime(); } - public void finishServerFiltersResponse() { - this.finishServerFiltersResponse = System.nanoTime(); + public void finishConsumerDecodeResponse() { + this.finishConsumerDecodeResponse = nanoTime(); } - public long getStartBusinessMethod() { - return startBusinessMethod; + public long calcConsumerDecodeResponse() { + return calc(finishConsumerDecodeResponse, startConsumerDecodeResponse); } - public void startBusinessMethod() { - this.startBusinessMethod = System.nanoTime(); + public void startProviderSendResponse() { + this.startProviderSendResponse = nanoTime(); } - public long getFinishBusiness() { - return finishBusiness; + public void finishProviderSendResponse() { + this.finishProviderSendResponse = nanoTime(); } - public void finishBusiness() { - this.finishBusiness = System.nanoTime(); + public long calcProviderSendResponse() { + return calc(finishProviderSendResponse, startProviderSendResponse); } - public long getFinish() { - return finish; + public void startBusinessExecute() { + this.startBusinessExecute = nanoTime(); } - public void finish() { - this.finish = System.nanoTime(); + public void finishBusinessExecute() { + this.finishBusinessExecute = nanoTime(); } - private double calc(long finish, long start) { - if (finish == 0 || start == 0) { - return Double.NaN; - } - - return finish - start; + public long calcBusinessExecute() { + return calc(finishBusinessExecute, startBusinessExecute); } - public double calcTotalTime() { - return calc(finish, start); + public void startConsumerConnection() { + this.startConsumerConnection = nanoTime(); } - public double calcInvocationPrepareTime() { - if (invocation.isConsumer() && !invocation.isEdge()) { - return calc(startHandlersRequest, start); - } - - return calc(startSchedule, start); + public void finishConsumerConnection() { + this.finishConsumerConnection = nanoTime(); } - public double calcHandlersRequestTime() { - if (invocation.isConsumer()) { - return calc(startClientFiltersRequest, startHandlersRequest); - } - - return calc(startBusinessMethod, startHandlersRequest); + public long calcConnection() { + return calc(finishConsumerConnection, startConsumerConnection); } - public double calcClientFiltersRequestTime() { - return calc(startSend, startClientFiltersRequest); + public void startConsumerSendRequest() { + this.startConsumerSendRequest = nanoTime(); } - public double calcServerFiltersRequestTime() { - return calc(startHandlersRequest, startServerFiltersRequest); + public void finishConsumerSendRequest() { + this.finishConsumerSendRequest = nanoTime(); } - public double calcSendRequestTime() { - return calc(finishWriteToBuffer, startSend); + public long calcConsumerSendRequest() { + return calc(finishConsumerSendRequest, startConsumerSendRequest); } - public double calcGetConnectionTime() { - return calc(finishGetConnection, startGetConnection); + public void startWaitResponse() { + this.startWaitResponse = nanoTime(); } - public double calcWriteToBufferTime() { - return calc(finishWriteToBuffer, finishGetConnection); + public void finishWaitResponse() { + this.finishWaitResponse = nanoTime(); } - public double calcReceiveResponseTime() { - return calc(finishReceiveResponse, finishWriteToBuffer); + public long calcWait() { + return calc(finishWaitResponse, startWaitResponse); } - public double calcWakeConsumer() { - return calc(startClientFiltersResponse, finishReceiveResponse); + public long calcTotal() { + return calc(finish, this.startCreateInvocation); } - public double calcClientFiltersResponseTime() { - return calc(finishClientFiltersResponse, startClientFiltersResponse); + public long getStartInMillis() { + return this.startInMillis; } - public double calcServerFiltersResponseTime() { - return calc(finishServerFiltersResponse, finishHandlersResponse); + public long getStart() { + return this.start; } - public double calcHandlersResponseTime() { - if (invocation.isConsumer()) { - return calc(finishHandlersResponse, finishClientFiltersResponse); + public static long calc(long finish, long start) { + if (finish == 0 || start == 0) { + return 0; } - return calc(finishHandlersResponse, finishBusiness); - } - - public double calcThreadPoolQueueTime() { - return calc(startExecution, startSchedule); + return finish - start; } - public double calcBusinessTime() { - return calc(finishBusiness, startBusinessMethod); + /* + * Holder for testing purpose + */ + protected long nanoTime() { + return System.nanoTime(); } - public double calcSendResponseTime() { - return calc(finish, finishServerFiltersResponse); + protected long millisTime() { + return System.currentTimeMillis(); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutBootListener.java b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutBootListener.java index 93429e3c7e3..6a5e929a8bd 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutBootListener.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutBootListener.java @@ -21,8 +21,6 @@ import org.apache.servicecomb.core.event.InvocationBusinessFinishEvent; import org.apache.servicecomb.core.event.InvocationBusinessMethodStartEvent; -import org.apache.servicecomb.core.event.InvocationHandlersStartEvent; -import org.apache.servicecomb.core.event.InvocationRunInExecutorStartEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; import org.apache.servicecomb.core.event.InvocationStartSendRequestEvent; import org.apache.servicecomb.core.event.InvocationTimeoutCheckEvent; @@ -35,8 +33,8 @@ import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; -@SuppressWarnings({"UnstableApiUsage", "unused"}) -public class InvocationTimeoutBootListener { +@SuppressWarnings({"unused"}) +public final class InvocationTimeoutBootListener { private static final Logger LOGGER = LoggerFactory.getLogger(InvocationTimeoutBootListener.class); public static final String PREFIX = "servicecomb.invocation.timeout.check"; @@ -74,18 +72,6 @@ public void onInvocationStartEvent(InvocationStartEvent event) { strategy.start(event.getInvocation()); } - @Subscribe - @EnableExceptionPropagation - public void onInvocationRunInExecutorStartEvent(InvocationRunInExecutorStartEvent event) { - strategy.startRunInExecutor(event.getInvocation()); - } - - @Subscribe - @EnableExceptionPropagation - public void onInvocationHandlersStartEvent(InvocationHandlersStartEvent event) { - strategy.startHandlers(event.getInvocation()); - } - @Subscribe @EnableExceptionPropagation public void onInvocationBusinessMethodStartEvent(InvocationBusinessMethodStartEvent event) { diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutStrategy.java b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutStrategy.java index 5e31c59136c..c530acfca29 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutStrategy.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutStrategy.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.core.invocation; -import static javax.ws.rs.core.Response.Status.REQUEST_TIMEOUT; +import static jakarta.ws.rs.core.Response.Status.REQUEST_TIMEOUT; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionCodes; @@ -37,10 +37,6 @@ default void startRunInExecutor(Invocation invocation) { checkTimeout(invocation); } - default void startHandlers(Invocation invocation) { - checkTimeout(invocation); - } - default void startBusinessMethod(Invocation invocation) { checkTimeout(invocation); } diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java b/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java index 6f4f8e2ff2d..ec589f37733 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java @@ -19,14 +19,16 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.exception.Exceptions; -import org.apache.servicecomb.foundation.common.utils.ExceptionUtils; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jakarta.ws.rs.core.Response.Status; + public abstract class ProducerInvocationFlow { private static final Logger LOGGER = LoggerFactory.getLogger(ProducerInvocationFlow.class); @@ -60,57 +62,49 @@ private void tryRunInvocation(Invocation invocation) { if (invocation == null) { return; } - - invocation.onStart(requestEx, startTime); - invocation.getMicroserviceMeta().getFilterChain() - .onFilter(invocation) - .whenComplete((response, Throwable) -> sendResponse(invocation, response)) - .whenComplete((response, Throwable) -> finishInvocation(invocation, response, Throwable)); - } - - private void finishInvocation(Invocation invocation, Response response, Throwable throwable) { - invocation.onFinish(response); - - tryLogException(invocation, throwable); - } - - private void tryLogException(Invocation invocation, Throwable throwable) { - if (throwable == null) { - return; - } - - throwable = Exceptions.unwrap(throwable); - if (requestEx == null) { - logException(invocation, throwable); + invocation.getInvocationStageTrace().startCreateInvocation(this.startTime); + invocation.getInvocationStageTrace().finishCreateInvocation(); + invocation.onStart(requestEx); + if (invocation.isEdge()) { + invocation.getMicroserviceMeta().getEdgeFilterChain() + .onFilter(invocation) + .whenComplete((response, throwable) -> { + if (throwable != null) { + // Server codec operates on Response. So the filter chain result should be Response and + // will never throw exception. + // Sometimes Server Codec will throw exception, e.g. Connection Closed. + LOGGER.error("Maybe a fatal bug that should be addressed or codec exception for {}.", + invocation.getInvocationQualifiedName(), throwable); + response = Response.createFail(new InvocationException(Status.INTERNAL_SERVER_ERROR, + new CommonExceptionData("Internal error, check logs for details."))); + } + endResponse(invocation, response); + finishInvocation(invocation, response); + }); return; } - - logException(invocation, requestEx, throwable); - } - - private void logException(Invocation invocation, Throwable throwable) { - if (Exceptions.isPrintInvocationStackTrace()) { - LOGGER.error("Failed to finish invocation, operation:{}.", invocation.getMicroserviceQualifiedName(), throwable); - return; - } - - LOGGER.error("Failed to finish invocation, operation:{}, message={}.", invocation.getMicroserviceQualifiedName(), - ExceptionUtils.getExceptionMessageWithoutTrace(throwable)); + invocation.getMicroserviceMeta().getProviderFilterChain() + .onFilter(invocation) + .whenComplete((response, throwable) -> { + if (throwable != null) { + // Server codec operates on Response. So the filter chain result should be Response and + // will never throw exception. + // Sometimes Server Codec will throw exception, e.g. Connection Closed. + LOGGER.error("Maybe a fatal bug that should be addressed or codec exception for {}.", + invocation.getInvocationQualifiedName(), throwable); + response = Response.createFail(new InvocationException(Status.INTERNAL_SERVER_ERROR, + new CommonExceptionData("Internal error, check logs for details."))); + } + endResponse(invocation, response); + finishInvocation(invocation, response); + }); } - private void logException(Invocation invocation, HttpServletRequestEx requestEx, Throwable throwable) { - if (Exceptions.isPrintInvocationStackTrace()) { - LOGGER.error("Failed to finish invocation, operation:{}, request uri:{}.", - invocation.getMicroserviceQualifiedName(), requestEx.getRequestURI(), throwable); - return; - } - - LOGGER.error("Failed to finish invocation, operation:{}, request uri:{}, message={}.", - invocation.getMicroserviceQualifiedName(), requestEx.getRequestURI(), - ExceptionUtils.getExceptionMessageWithoutTrace(throwable)); + private void finishInvocation(Invocation invocation, Response response) { + invocation.onFinish(response); } protected abstract Invocation sendCreateInvocationException(Throwable throwable); - protected abstract void sendResponse(Invocation invocation, Response response); + protected abstract void endResponse(Invocation invocation, Response response); } diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtils.java b/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtils.java index d906de63ba5..2d8b37c997a 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtils.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtils.java @@ -88,7 +88,7 @@ public static Endpoint parse(String uriEndpoint) { } /** - *
+   * {@code
    *   http://xxx  -> rest://xxx
    *   https://xxx -> rest://xxx?sslEnabled=true
    *
@@ -98,7 +98,7 @@ public static Endpoint parse(String uriEndpoint) {
    *   xxx         -> rest://xxx:port?protocol=http2
    *   xxx?a=a1    -> rest://xxx:port?a=a1&protocol=http2
    *   other://xxx -> other://xxx
-   * 
+ * } * * This method provided for convenience of handling user input endpoints, and do not have a strict meaning. Make sure all unit test cases * work before change. diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategy.java b/core/src/main/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategy.java index 986c085b394..6d55c7a7f51 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategy.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategy.java @@ -78,7 +78,7 @@ public void start(Invocation invocation) { return; } - long startTimeMillis = invocation.getInvocationStageTrace().getStartTimeMillis(); + long startTimeMillis = invocation.getInvocationStageTrace().getStartInMillis(); String contextChainStartTime = invocation.getContext(CHAIN_START_TIME); if (StringUtils.isEmpty(contextChainStartTime)) { invocation.addContext(CHAIN_START_TIME, String.valueOf(startTimeMillis)); diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/LocalOpenAPIRegistry.java b/core/src/main/java/org/apache/servicecomb/core/provider/LocalOpenAPIRegistry.java new file mode 100644 index 00000000000..14a27530c92 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/LocalOpenAPIRegistry.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.servicecomb.core.provider; + +import java.util.Collections; +import java.util.Map; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.core.provider.OpenAPIRegistryManager.OpenAPIChangeListener; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +import io.swagger.v3.oas.models.OpenAPI; + +/** + * register and load OpenAPI from local file store or memory + */ +public class LocalOpenAPIRegistry implements OpenAPIRegistry { + private static final Logger LOGGER = LoggerFactory.getLogger(LocalOpenAPIRegistry.class); + + // first key : appId + // second key: microservice short name + // third key : schemaId + private final Map>> apps = new ConcurrentHashMapEx<>(); + + private final Environment environment; + + private OpenAPIChangeListener openAPIChangeListener; + + public LocalOpenAPIRegistry(Environment environment) { + this.environment = environment; + } + + @Override + public boolean enabled() { + return true; + } + + @Override + public void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api) { + apps.computeIfAbsent(application, k -> new ConcurrentHashMapEx<>()) + .computeIfAbsent(serviceName, k -> new ConcurrentHashMapEx<>()) + .put(schemaId, api); + openAPIChangeListener.onOpenAPIChanged(application, serviceName); + LOGGER.info("register swagger appId={}, name={}, schemaId={}.", + application, serviceName, schemaId); + } + + /** + * Method for retrieve myself schema contents. + */ + public Map loadOpenAPI() { + return loadOpenAPI(BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment)); + } + + @Override + public Map loadOpenAPI(String application, String serviceName) { + if (apps.get(application) != null && apps.get(application).get(serviceName) != null) { + return apps.get(application).get(serviceName); + } + return Collections.emptyMap(); + } + + @Override + public void setOpenAPIChangeListener(OpenAPIChangeListener listener) { + this.openAPIChangeListener = listener; + } + + @Override + public int getOrder() { + return -10000; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistry.java b/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistry.java new file mode 100644 index 00000000000..440c297d7ae --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistry.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.servicecomb.core.provider; + +import java.util.Map; + +import org.apache.servicecomb.core.provider.OpenAPIRegistryManager.OpenAPIChangeListener; +import org.springframework.core.Ordered; + +import io.swagger.v3.oas.models.OpenAPI; + +/** + * Register and load OpenAPI extensions. + */ +public interface OpenAPIRegistry extends Ordered { + String CONFIG_PREFIX = "servicecomb.openAPI.registry"; + + boolean enabled(); + + void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api); + + Map loadOpenAPI(String application, String serviceName); + + void setOpenAPIChangeListener(OpenAPIChangeListener listener); +} diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistryManager.java b/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistryManager.java new file mode 100644 index 00000000000..c357e6a24ae --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistryManager.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.servicecomb.core.provider; + +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.FilenameUtils; +import org.apache.servicecomb.foundation.common.utils.ResourceUtil; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.CollectionUtils; + +import io.swagger.v3.oas.models.OpenAPI; + +/** + * Register and load OpenAPI from various OpenAPIRegistry + */ +public class OpenAPIRegistryManager { + private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIRegistryManager.class); + + public interface OpenAPIChangeListener { + void onOpenAPIChanged(String application, String serviceName); + } + + private List openAPIRegistries; + + private final List changeListeners = new ArrayList<>(); + + @Autowired + public void setOpenAPIRegistries(List openAPIRegistries) { + List target = new ArrayList<>(openAPIRegistries.size()); + for (OpenAPIRegistry registry : openAPIRegistries) { + if (registry.enabled()) { + registry.setOpenAPIChangeListener(this::onOpenAPIChanged); + target.add(registry); + } + } + this.openAPIRegistries = target; + } + + public void addOpenAPIChangeListener(OpenAPIChangeListener changeListener) { + this.changeListeners.add(changeListener); + } + + public void onOpenAPIChanged(String application, String serviceName) { + for (OpenAPIChangeListener listener : this.changeListeners) { + try { + listener.onOpenAPIChanged(application, serviceName); + } catch (Exception e) { + LOGGER.warn("event process error {}/{}, {}", application, serviceName, e.getMessage()); + } + } + } + + public void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api) { + for (OpenAPIRegistry registry : this.openAPIRegistries) { + registry.registerOpenAPI(application, serviceName, schemaId, api); + } + } + + public void registerOpenAPI(String application, String serviceName, String schemaId, Class cls) { + OpenAPI api = SwaggerGenerator.generate(cls); + registerOpenAPI(application, serviceName, schemaId, api); + } + + public void registerOpenAPIInLocation(String application, String serviceName, String swaggersLocation) { + try { + List resourceUris = ResourceUtil.findResourcesBySuffix(swaggersLocation, ".yaml"); + if (resourceUris.isEmpty()) { + return; + } + for (URI uri : resourceUris) { + URL url = uri.toURL(); + OpenAPI swagger = SwaggerUtils.parseAndValidateSwagger(url); + String schemaId = FilenameUtils.getBaseName(url.getPath()); + registerOpenAPI(application, serviceName, schemaId, swagger); + } + } catch (Throwable e) { + throw new IllegalStateException(String.format( + "failed to register swaggers, microserviceName=%s, location=%s.", + serviceName, swaggersLocation), e); + } + } + + public Map loadOpenAPI(String appId, String microserviceName) { + for (OpenAPIRegistry registry : this.openAPIRegistries) { + Map result = registry.loadOpenAPI(appId, microserviceName); + if (!CollectionUtils.isEmpty(result)) { + return result; + } + } + return Collections.emptyMap(); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/RegistryOpenAPIRegistry.java b/core/src/main/java/org/apache/servicecomb/core/provider/RegistryOpenAPIRegistry.java new file mode 100644 index 00000000000..cc5e6fe22dd --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/RegistryOpenAPIRegistry.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.provider; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.core.provider.OpenAPIRegistryManager.OpenAPIChangeListener; +import org.apache.servicecomb.registry.DiscoveryManager; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.router.util.VersionCompareUtil; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; + +import io.swagger.v3.oas.models.OpenAPI; +import jakarta.ws.rs.core.Response.Status; + +/** + * register and load OpenAPI from registration and discovery registry. + */ +public class RegistryOpenAPIRegistry implements OpenAPIRegistry { + private DiscoveryManager discoveryManager; + + private Environment environment; + + @Autowired + public void setDiscoveryManager(DiscoveryManager discoveryManager) { + this.discoveryManager = discoveryManager; + } + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + + @Override + public boolean enabled() { + return environment.getProperty(OpenAPIRegistry.CONFIG_PREFIX + ".registry.enabled", boolean.class, false); + } + + @Override + public void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api) { + // do noting + } + + @Override + public Map loadOpenAPI(String application, String serviceName) { + List discoveryInstances = + discoveryManager.findServiceInstances(application, serviceName); + if (discoveryInstances.isEmpty()) { + throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "no instances"); + } + discoveryInstances.sort((a, b) -> VersionCompareUtil.compareVersion(b.getVersion(), a.getVersion())); + + Map result = new HashMap<>(); + String version = null; + for (DiscoveryInstance instance : discoveryInstances) { + if (version != null && !version.equals(instance.getVersion())) { + break; + } + version = instance.getVersion(); + instance.getSchemas().forEach((k, v) -> result.computeIfAbsent(k, (key) -> SwaggerUtils.parseSwagger(v))); + } + return result; + } + + @Override + public void setOpenAPIChangeListener(OpenAPIChangeListener listener) { + this.discoveryManager.addInstanceChangeListener( + (registryName, application, serviceName, instances) -> { + if (CollectionUtils.isEmpty(instances)) { + return; + } + listener.onOpenAPIChanged(application, serviceName); + }); + } + + @Override + public int getOrder() { + return -8000; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/AbstractConsumerProvider.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/AbstractConsumerProvider.java deleted file mode 100644 index c9093c330dc..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/AbstractConsumerProvider.java +++ /dev/null @@ -1,26 +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.servicecomb.core.provider.consumer; - -import org.apache.servicecomb.core.ConsumerProvider; - -public abstract class AbstractConsumerProvider implements ConsumerProvider { - @Override - public void init() { - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ConsumerProviderManager.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ConsumerProviderManager.java index b1a6f8ec956..615063bd3b4 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ConsumerProviderManager.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ConsumerProviderManager.java @@ -17,23 +17,70 @@ package org.apache.servicecomb.core.provider.consumer; -import java.util.ArrayList; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.List; -import org.apache.servicecomb.core.ConsumerProvider; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.core.provider.OpenAPIRegistryManager; +import org.apache.servicecomb.foundation.common.utils.ResourceUtil; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; public class ConsumerProviderManager { - private final List consumerProviderList = new ArrayList<>( - SPIServiceUtils.getOrLoadSortedService(ConsumerProvider.class)); + private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerProviderManager.class); - public List getConsumerProviderList() { - return consumerProviderList; + private final OpenAPIRegistryManager openAPIRegistryManager; + + private final Environment environment; + + public ConsumerProviderManager(Environment environment, OpenAPIRegistryManager openAPIRegistryManager) { + this.environment = environment; + this.openAPIRegistryManager = openAPIRegistryManager; } public void init() throws Exception { - for (ConsumerProvider provider : consumerProviderList) { - provider.init(); + registerSwaggerFromApplications(); + registerSwaggerFromMicroservices(); + } + + private void registerSwaggerFromApplications() { + try { + List resourceUris = ResourceUtil.findResourcesBySuffix("applications", ".yaml"); + for (URI uri : resourceUris) { + String path = uri.toURL().getPath(); + String[] segments = path.split("/"); + if (segments.length < 4 || !"applications".equals(segments[segments.length - 4])) { + continue; + } + openAPIRegistryManager.registerOpenAPI(segments[segments.length - 3], segments[segments.length - 2], + segments[segments.length - 1].substring(0, segments[segments.length - 1].indexOf(".yaml")), + SwaggerUtils.parseAndValidateSwagger(uri.toURL())); + } + } catch (IOException | URISyntaxException e) { + LOGGER.error("Load schema ids failed from applications. {}.", e.getMessage()); + } + } + + private void registerSwaggerFromMicroservices() { + try { + List resourceUris = ResourceUtil.findResourcesBySuffix("microservices", ".yaml"); + for (URI uri : resourceUris) { + String path = uri.toURL().getPath(); + String[] segments = path.split("/"); + if (segments.length < 3 || !"microservices".equals(segments[segments.length - 3])) { + continue; + } + openAPIRegistryManager.registerOpenAPI(BootStrapProperties.readApplication(environment), + segments[segments.length - 2], + segments[segments.length - 1].substring(0, segments[segments.length - 1].indexOf(".yaml")), + SwaggerUtils.parseAndValidateSwagger(uri.toURL())); + } + } catch (IOException | URISyntaxException e) { + LOGGER.error("Load schema ids failed from microservices. {}.", e.getMessage()); } } } diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java index 49a990a2d0c..152f43a5034 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java @@ -18,25 +18,14 @@ package org.apache.servicecomb.core.provider.consumer; import static org.apache.servicecomb.core.exception.Exceptions.toConsumerResponse; -import static org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory.CONSUMER_INNER_STATUS_CODE; import java.lang.reflect.Method; import java.lang.reflect.Type; -import java.time.Duration; import java.util.Map; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; - -import javax.annotation.Nonnull; -import javax.ws.rs.core.Response.Status; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; @@ -44,133 +33,81 @@ import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; -import org.apache.servicecomb.core.governance.GovernanceConfiguration; -import org.apache.servicecomb.core.governance.MatchType; -import org.apache.servicecomb.core.governance.RetryContext; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; -import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.governance.handler.RetryHandler; -import org.apache.servicecomb.governance.handler.ext.FailurePredictor; -import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.swagger.invocation.AsyncResponse; import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.annotations.VisibleForTesting; -import com.netflix.config.DynamicPropertyFactory; -import io.github.resilience4j.decorators.Decorators; -import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; -import io.github.resilience4j.retry.Retry; -import io.github.resilience4j.retry.RetryConfig; -import io.github.resilience4j.retry.RetryRegistry; import io.vertx.core.Context; +import jakarta.ws.rs.core.Response.Status; public final class InvokerUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(InvokerUtils.class); - - private static final Object LOCK = new Object(); - - private static volatile ScheduledExecutorService reactiveRetryPool; - - private static ScheduledExecutorService getOrCreateRetryPool() { - if (reactiveRetryPool == null) { - synchronized (LOCK) { - if (reactiveRetryPool == null) { - reactiveRetryPool = Executors.newScheduledThreadPool(2, new ThreadFactory() { - private final AtomicInteger count = new AtomicInteger(0); - - @Override - public Thread newThread(Runnable r) { - Thread thread = new Thread(r, "reactive-retry-pool-thread-" + count.getAndIncrement()); - // avoid block shutdown - thread.setDaemon(true); - return thread; - } - }); - } - } - } - return reactiveRetryPool; - } - - private static final boolean ENABLE_EVENT_LOOP_BLOCKING_CALL_CHECK = - DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.invocation.enableEventLoopBlockingCallCheck", true).get(); - @SuppressWarnings({"unchecked"}) - public static T syncInvoke(String microserviceName, String microserviceVersion, String transport, + public static T syncInvoke(String microserviceName, String transport, String schemaId, String operationId, Map swaggerArguments, Type responseType) { - Invocation invocation = createInvocation(microserviceName, microserviceVersion, transport, schemaId, operationId, + Invocation invocation = createInvocation(microserviceName, transport, schemaId, operationId, swaggerArguments, responseType); return (T) syncInvoke(invocation); } - public static void reactiveInvoke(String microserviceName, String microserviceVersion, String transport, + public static void reactiveInvoke(String microserviceName, String transport, String schemaId, String operationId, Map swaggerArguments, Type responseType, AsyncResponse asyncResp) { - Invocation invocation = createInvocation(microserviceName, microserviceVersion, transport, schemaId, operationId, + Invocation invocation = createInvocation(microserviceName, transport, schemaId, operationId, swaggerArguments, responseType); reactiveInvoke(invocation, asyncResp); } public static T syncInvoke(String microserviceName, String schemaId, String operationId, Map swaggerArguments, Type responseType) { - return syncInvoke(microserviceName, null, null, + return syncInvoke(microserviceName, null, schemaId, operationId, swaggerArguments, responseType); } - @Deprecated public static void reactiveInvoke(String microserviceName, String schemaId, String operationId, Map swaggerArguments, Type responseType, AsyncResponse asyncResp) { - reactiveInvoke(microserviceName, null, null, + reactiveInvoke(microserviceName, null, schemaId, operationId, swaggerArguments, responseType, asyncResp); } - private static Invocation createInvocation(String microserviceName, String microserviceVersion, String transport, + public static Invocation createInvocation(String microserviceName, String transport, String schemaId, String operationId, Map swaggerArguments, Type responseType) { + long startCreateInvocation = System.nanoTime(); MicroserviceReferenceConfig microserviceReferenceConfig = SCBEngine.getInstance() - .createMicroserviceReferenceConfig(microserviceName, microserviceVersion); - MicroserviceMeta microserviceMeta = microserviceReferenceConfig.getLatestMicroserviceMeta(); + .getOrCreateReferenceConfig(microserviceName); + if (microserviceReferenceConfig == null) { + throw new InvocationException(Status.INTERNAL_SERVER_ERROR, + new CommonExceptionData(String.format("Failed to invoke service %s. Maybe service" + + " not registered or no active instance.", microserviceName))); + } + MicroserviceMeta microserviceMeta = microserviceReferenceConfig.getMicroserviceMeta(); SchemaMeta schemaMeta = microserviceMeta.ensureFindSchemaMeta(schemaId); OperationMeta operationMeta = schemaMeta.ensureFindOperation(operationId); ReferenceConfig referenceConfig = microserviceReferenceConfig.createReferenceConfig(transport, operationMeta); InvocationRuntimeType invocationRuntimeType = operationMeta.buildBaseConsumerRuntimeType(); invocationRuntimeType.setSuccessResponseType(responseType); - return InvocationFactory + Invocation result = InvocationFactory .forConsumer(referenceConfig, operationMeta, invocationRuntimeType, swaggerArguments); + result.getInvocationStageTrace().startCreateInvocation(startCreateInvocation); + result.getInvocationStageTrace().finishCreateInvocation(); + return result; } /** * * use of this method , the response type can not be determined. - * use {@link #syncInvoke(String, String, String, Map, Type)} instead. - * - */ - @Deprecated - public static Object syncInvoke(String microserviceName, String schemaId, String operationId, - Map swaggerArguments) { - return syncInvoke(microserviceName, null, null, schemaId, operationId, swaggerArguments); - } - - /** - * - * use of this method , the response type can not be determined. - * use {@link #syncInvoke(String, String, String, String, String, Map, Type)} instead. + * use {@link #syncInvoke(String, String, String, String, Map, Type)} instead. * */ @Deprecated - public static Object syncInvoke(String microserviceName, String microserviceVersion, String transport, + public static Object syncInvoke(String microserviceName, String transport, String schemaId, String operationId, Map swaggerArguments) { - return syncInvoke(microserviceName, microserviceVersion, transport, schemaId, operationId, swaggerArguments, + return syncInvoke(microserviceName, transport, schemaId, operationId, swaggerArguments, null); } @@ -193,49 +130,15 @@ public static boolean isInEventLoop() { * This is an internal API, caller make sure already invoked SCBEngine.ensureStatusUp */ public static Response innerSyncInvoke(Invocation invocation) { - if (ENABLE_EVENT_LOOP_BLOCKING_CALL_CHECK && isInEventLoop()) { + if (isInEventLoop() && + SCBEngine.getInstance() + .getEnvironment() + .getProperty("servicecomb.invocation.enableEventLoopBlockingCallCheck", boolean.class, true)) { throw new IllegalStateException("Can not execute sync logic in event loop."); } return toSync(invoke(invocation), invocation.getWaitTime()); } - private static void updateRetryStatus(Invocation invocation) { - if (invocation.isFinished()) { - invocation.reset(); - if (invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE) != null - && (boolean) invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE)) { - // clear last server to avoid using user defined endpoint - invocation.setEndpoint(null); - } - RetryContext retryContext = invocation.getLocalContext(RetryContext.RETRY_CONTEXT); - retryContext.incrementRetry(); - return; - } - - invocation.addLocalContext(RetryContext.RETRY_CONTEXT, - new RetryContext(GovernanceConfiguration.getRetrySameServer(invocation.getMicroserviceName()))); - } - - private static boolean isCompatibleRetryEnabled(Invocation invocation) { - // maxAttempts must be greater than or equal to 1 - return GovernanceConfiguration.isRetryEnabled(invocation.getMicroserviceName()) - && GovernanceConfiguration.getRetryNextServer(invocation.getMicroserviceName()) - + GovernanceConfiguration.getRetrySameServer(invocation.getMicroserviceName()) > 0; - } - - private static Retry getOrCreateCompatibleRetry(Invocation invocation) { - RetryConfig retryConfig = RetryConfig.custom() - // max attempts include the first call - .maxAttempts(GovernanceConfiguration.getRetryNextServer(invocation.getMicroserviceName()) - + GovernanceConfiguration.getRetrySameServer(invocation.getMicroserviceName()) + 1) - .retryOnResult(InvokerUtils::canRetryForStatusCode) - .retryOnException(InvokerUtils::canRetryForException) - .waitDuration(Duration.ofMillis(1)) - .build(); - RetryRegistry retryRegistry = RetryRegistry.of(retryConfig); - return retryRegistry.retry(invocation.getMicroserviceName()); - } - /** * This is an internal API, caller make sure already invoked SCBEngine.ensureStatusUp */ @@ -249,27 +152,11 @@ public static void reactiveInvoke(Invocation invocation, AsyncResponse asyncResp }); } - private static void decorateReactiveRetry(Invocation invocation, DecorateCompletionStage dcs, - GovernanceRequestExtractor request) { - // governance implementations. - RetryHandler retryHandler = BeanUtils.getBean(RetryHandler.class); - Retry retry = retryHandler.getActuator(request); - if (retry != null) { - dcs.withRetry(retry, getOrCreateRetryPool()); - } - - if (isCompatibleRetryEnabled(invocation)) { - // compatible implementation for retry in load balance module in old versions. - retry = getOrCreateCompatibleRetry(invocation); - dcs.withRetry(retry, getOrCreateRetryPool()); - } - } - - public static boolean isSyncMethod(@Nonnull Method method) { + public static boolean isSyncMethod(Method method) { return !isAsyncMethod(method); } - public static boolean isAsyncMethod(@Nonnull Method method) { + public static boolean isAsyncMethod(Method method) { // currently only support CompletableFuture for async method definition return method.getReturnType().equals(CompletableFuture.class); } @@ -295,85 +182,10 @@ public static T toSync(CompletableFuture future, long waitInMillis) { * NOTE: this method should never throw exception directly */ public static CompletableFuture invoke(Invocation invocation) { - Supplier> next = invokeImpl(invocation); - DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); - GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); - - decorateReactiveRetry(invocation, dcs, request); - - CompletableFuture result = new CompletableFuture<>(); - dcs.get().whenComplete((r, e) -> { - ContextUtils.setInvocationContext(invocation.getParentContext()); - - if (e == null) { - result.complete(r); - return; - } - - String message = String.format("invoke failed, operation %s, trace id %s", - invocation.getMicroserviceQualifiedName(), - invocation.getTraceId()); - LOGGER.error(message, e); - Response response = Response.createConsumerFail(e, message); - invocation.onFinish(response); - result.complete(response); - }); - return result; - } - - private static Supplier> invokeImpl(Invocation invocation) { - return () -> { - invocation.onStart(null, System.nanoTime()); - updateRetryStatus(invocation); - invocation.onStartHandlersRequest(); - return invocation.getMicroserviceMeta().getFilterChain() - .onFilter(invocation) - .exceptionally(throwable -> toConsumerResponse(invocation, throwable)) - .whenComplete((response, throwable) -> finishInvocation(invocation, response)); - }; - } - - private static void finishInvocation(Invocation invocation, Response ar) { - invocation.getInvocationStageTrace().finishHandlersResponse(); - invocation.onFinish(ar); - - if (ar.isFailed()) { - // re-throw exception to make sure retry based on exception - // for InvocationException, users can configure status code for retry - // for 490, details error are wrapped, need re-throw - - if (!(ar.getResult() instanceof InvocationException)) { - throw AsyncUtils.rethrow(ar.getResult()); - } - - if (((InvocationException) ar.getResult()).getStatusCode() == CONSUMER_INNER_STATUS_CODE) { - throw AsyncUtils.rethrow(ar.getResult()); - } - } - } - - @VisibleForTesting - static boolean canRetryForException(Throwable e) { - if (e instanceof InvocationException && ((InvocationException) e).getStatusCode() == Status.SERVICE_UNAVAILABLE - .getStatusCode()) { - return true; - } - return FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, e); - } - - @VisibleForTesting - static boolean canRetryForStatusCode(Object response) { - // retry on status code 503 - if (!(response instanceof Response resp)) { - return false; - } - if (!resp.isFailed()) { - return false; - } - if (resp.getResult() instanceof InvocationException) { - InvocationException e = resp.getResult(); - return e.getStatusCode() == 503; - } - return false; + invocation.onStart(null); + return invocation.getMicroserviceMeta().getConsumerFilterChain() + .onFilter(invocation) + .exceptionally(throwable -> toConsumerResponse(invocation, throwable)) + .whenComplete((response, throwable) -> invocation.onFinish(response)); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/MicroserviceReferenceConfig.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/MicroserviceReferenceConfig.java index d2670476ae2..60948bd130d 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/MicroserviceReferenceConfig.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/MicroserviceReferenceConfig.java @@ -16,74 +16,38 @@ */ package org.apache.servicecomb.core.provider.consumer; -import java.util.Objects; -import java.util.function.Supplier; - -import org.apache.servicecomb.core.definition.ConsumerMicroserviceVersionsMeta; -import org.apache.servicecomb.core.definition.CoreMetaUtils; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; -import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.consumer.MicroserviceVersion; -import org.apache.servicecomb.registry.consumer.MicroserviceVersionRule; -import org.apache.servicecomb.registry.consumer.MicroserviceVersions; -import org.apache.servicecomb.registry.consumer.StaticMicroserviceVersions; /** - *
- *   when the list data changed, MicroserviceReferenceConfig should rebuild:
- *     1.versionRule
- *     2.latestMicroserviceMeta
- *     3.microservice deleted
- * 
+ * microservice meta data for consumer. */ public class MicroserviceReferenceConfig { - private final ConsumerMicroserviceVersionsMeta microserviceVersionsMeta; - - private final MicroserviceVersionRule microserviceVersionRule; - - private final MicroserviceVersion latestMicroserviceVersion; - - private final MicroserviceMeta latestMicroserviceMeta; - - private final String versionRule; - - // return true means changed - private final Supplier versionRuleChangedChecker; - - public MicroserviceReferenceConfig(ConsumerMicroserviceVersionsMeta microserviceVersionsMeta, String versionRule) { - this.microserviceVersionsMeta = microserviceVersionsMeta; + private final String appId; - this.versionRule = - versionRule != null ? versionRule : microserviceVersionsMeta.getMicroserviceConfig().getVersionRule(); - this.versionRuleChangedChecker = this.versionRule != null ? this::checkByConfig : this::notChange; + private final String microserviceName; - microserviceVersionRule = microserviceVersionsMeta.getMicroserviceVersions() - .getOrCreateMicroserviceVersionRule(this.versionRule); - latestMicroserviceVersion = microserviceVersionRule.getLatestMicroserviceVersion(); - latestMicroserviceMeta = - latestMicroserviceVersion != null ? CoreMetaUtils.getMicroserviceMeta(latestMicroserviceVersion) : null; - } - - private Boolean notChange() { - return false; - } + private final MicroserviceMeta microserviceMeta; - private Boolean checkByConfig() { - return !Objects.equals(versionRule, microserviceVersionsMeta.getMicroserviceConfig().getVersionRule()); + public MicroserviceReferenceConfig( + String appId, + String microserviceName, + MicroserviceMeta microserviceMeta) { + this.appId = appId; + this.microserviceName = microserviceName; + this.microserviceMeta = microserviceMeta; } - public MicroserviceMeta getLatestMicroserviceMeta() { - if (latestMicroserviceVersion == null) { + public MicroserviceMeta getMicroserviceMeta() { + if (microserviceMeta == null) { throw new IllegalStateException( String.format( - "Probably invoke a service before it is registered, or no instance found for it, appId=%s, name=%s, versionRule=%s.", - microserviceVersionRule.getAppId(), - microserviceVersionRule.getMicroserviceName(), - versionRule)); + "Probably invoke a service before it is registered, or no instance found for it, appId=%s, name=%s.", + appId, + microserviceName)); } - return latestMicroserviceMeta; + return microserviceMeta; } public ReferenceConfig createReferenceConfig(OperationMeta operationMeta) { @@ -94,25 +58,7 @@ public ReferenceConfig createReferenceConfig(String transport, OperationMeta ope if (transport == null) { transport = operationMeta.getConfig().getTransport(); } - final ReferenceConfig referenceConfig = new ReferenceConfig(transport, versionRule); - mark3rdPartyService(operationMeta, referenceConfig); + final ReferenceConfig referenceConfig = new ReferenceConfig(transport); return referenceConfig; } - - private void mark3rdPartyService(OperationMeta operationMeta, ReferenceConfig referenceConfig) { - final MicroserviceVersions microserviceVersions = DiscoveryManager.INSTANCE - .getOrCreateMicroserviceVersions( - operationMeta.getMicroserviceMeta().getAppId(), - operationMeta.getMicroserviceName()); - referenceConfig.setThirdPartyService(microserviceVersions instanceof StaticMicroserviceVersions); - } - - public boolean isExpired() { - // 1.microservice deleted - // 2.latest version changed - // 3.versionRule configuration changed - return microserviceVersionsMeta.getMicroserviceVersions().isWaitingDelete() || - latestMicroserviceVersion != microserviceVersionRule.getLatestMicroserviceVersion() || - versionRuleChangedChecker.get(); - } } diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfig.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfig.java index 0932f923f90..3b6ad24e610 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfig.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfig.java @@ -21,13 +21,8 @@ public class ReferenceConfig { protected String transport; - protected String versionRule; - - private boolean thirdPartyService; - - public ReferenceConfig(String transport, String versionRule) { + public ReferenceConfig(String transport) { this.transport = transport; - this.versionRule = versionRule; } public String getTransport() { @@ -37,21 +32,4 @@ public String getTransport() { public void setTransport(String transport) { this.transport = transport; } - - public boolean is3rdPartyService() { - return isThirdPartyService(); - } - - public String getVersionRule() { - return versionRule; - } - - public boolean isThirdPartyService() { - return thirdPartyService; - } - - public ReferenceConfig setThirdPartyService(boolean thirdPartyService) { - this.thirdPartyService = thirdPartyService; - return this; - } } diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfigManager.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfigManager.java new file mode 100644 index 00000000000..dffecfd62bf --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfigManager.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.provider.consumer; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.SCBEngine.CreateMicroserviceMetaEvent; +import org.apache.servicecomb.core.definition.ConsumerMicroserviceVersionsMeta; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.core.provider.OpenAPIRegistryManager; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.vertx.executor.VertxWorkerExecutor; +import org.apache.servicecomb.registry.definition.MicroserviceNameParser; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.beans.factory.annotation.Autowired; + +import io.swagger.v3.oas.models.OpenAPI; +import jakarta.ws.rs.core.Response.Status; + +public class ReferenceConfigManager { + // application -> microservice name + private final Map> referenceConfigs = new ConcurrentHashMapEx<>(); + + private final Map> referenceConfigsLocks = new ConcurrentHashMapEx<>(); + + private OpenAPIRegistryManager openAPIRegistryManager; + + @Autowired + public void setOpenAPIRegistryManager(OpenAPIRegistryManager openAPIRegistryManager) { + this.openAPIRegistryManager = openAPIRegistryManager; + this.openAPIRegistryManager.addOpenAPIChangeListener(this::onOpenAPIChanged); + } + + private void onOpenAPIChanged(String application, String serviceName) { + if (referenceConfigs.get(application) != null && referenceConfigs.get(application).get(serviceName) != null) { + MicroserviceReferenceConfig config = buildMicroserviceReferenceConfig(SCBEngine.getInstance(), application, + serviceName); + referenceConfigs.get(application).put(serviceName, config); + } + } + + public CompletableFuture getOrCreateReferenceConfigAsync + (SCBEngine scbEngine, String qualifiedName) { + MicroserviceNameParser parser = parseMicroserviceName(scbEngine, qualifiedName); + MicroserviceReferenceConfig config = referenceConfigs.computeIfAbsent(parser.getAppId(), + app -> new ConcurrentHashMapEx<>()) + .get(parser.getMicroserviceName()); + + if (config != null) { + return CompletableFuture.completedFuture(config); + } + + if (InvokerUtils.isInEventLoop()) { + CompletableFuture result = new CompletableFuture<>(); + VertxWorkerExecutor executor = new VertxWorkerExecutor(); + executor.execute(() -> { + synchronized (referenceConfigsLocks.computeIfAbsent(parser.getAppId(), key -> new ConcurrentHashMapEx<>()) + .computeIfAbsent(parser.getMicroserviceName(), key -> new Object())) { + try { + MicroserviceReferenceConfig temp = referenceConfigs.get(parser.getAppId()) + .get(parser.getMicroserviceName()); + if (temp != null) { + result.complete(temp); + return; + } + temp = buildMicroserviceReferenceConfig(scbEngine, parser.getAppId(), + parser.getMicroserviceName()); + referenceConfigs.get(parser.getAppId()).put(parser.getMicroserviceName(), temp); + result.complete(temp); + } catch (Exception e) { + result.completeExceptionally(e); + } + } + }); + return result; + } else { + synchronized (referenceConfigsLocks.computeIfAbsent(parser.getAppId(), key -> new ConcurrentHashMapEx<>()) + .computeIfAbsent(parser.getMicroserviceName(), key -> new Object())) { + MicroserviceReferenceConfig temp = referenceConfigs.get(parser.getAppId()) + .get(parser.getMicroserviceName()); + if (temp != null) { + return CompletableFuture.completedFuture(temp); + } + temp = buildMicroserviceReferenceConfig(scbEngine, parser.getAppId(), + parser.getMicroserviceName()); + referenceConfigs.get(parser.getAppId()).put(parser.getMicroserviceName(), temp); + return CompletableFuture.completedFuture(temp); + } + } + } + + public MicroserviceReferenceConfig getOrCreateReferenceConfig(SCBEngine scbEngine, String qualifiedName) { + MicroserviceNameParser parser = parseMicroserviceName(scbEngine, qualifiedName); + MicroserviceReferenceConfig config = referenceConfigs.computeIfAbsent(parser.getAppId(), + app -> new ConcurrentHashMapEx<>()) + .get(parser.getMicroserviceName()); + if (config == null) { + synchronized (referenceConfigsLocks.computeIfAbsent(parser.getAppId(), key -> new ConcurrentHashMapEx<>()) + .computeIfAbsent(parser.getMicroserviceName(), key -> new Object())) { + config = referenceConfigs.get(parser.getAppId()).get(parser.getMicroserviceName()); + if (config != null) { + return config; + } + config = buildMicroserviceReferenceConfig(scbEngine, parser.getAppId(), parser.getMicroserviceName()); + referenceConfigs.get(parser.getAppId()).put(parser.getMicroserviceName(), config); + return config; + } + } + return config; + } + + private MicroserviceNameParser parseMicroserviceName(SCBEngine scbEngine, String microserviceName) { + return new MicroserviceNameParser(scbEngine.getAppId(), microserviceName); + } + + private MicroserviceReferenceConfig buildMicroserviceReferenceConfig(SCBEngine engine, + String application, String microserviceName) { + ConsumerMicroserviceVersionsMeta microserviceVersionsMeta = new ConsumerMicroserviceVersionsMeta(engine); + MicroserviceMeta microserviceMeta = new MicroserviceMeta(engine, application, microserviceName, true); + microserviceMeta.setConsumerFilterChain(engine.getFilterChainsManager() + .findConsumerChain(application, microserviceName)); + microserviceMeta.setEdgeFilterChain(engine.getFilterChainsManager() + .findEdgeChain(application, microserviceName)); + microserviceMeta.setMicroserviceVersionsMeta(microserviceVersionsMeta); + + Map schemas = this.openAPIRegistryManager.loadOpenAPI(application, microserviceName); + for (Entry entry : schemas.entrySet()) { + OpenAPI swagger = entry.getValue(); + if (swagger != null) { + microserviceMeta.registerSchemaMeta(entry.getKey(), entry.getValue()); + continue; + } + throw new InvocationException(Status.INTERNAL_SERVER_ERROR, + String.format("Swagger %s/%s/%s can not be empty or load swagger failed.", + application, microserviceName, entry.getKey())); + } + + EventManager.getEventBus().post(new CreateMicroserviceMetaEvent(microserviceMeta)); + return new MicroserviceReferenceConfig(application, + microserviceName, microserviceMeta); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/SyncResponseExecutor.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/SyncResponseExecutor.java index cf65384d3b1..8e86d9d7f90 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/SyncResponseExecutor.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/SyncResponseExecutor.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.core.provider.consumer; -import static javax.ws.rs.core.Response.Status.REQUEST_TIMEOUT; +import static jakarta.ws.rs.core.Response.Status.REQUEST_TIMEOUT; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; @@ -98,8 +98,7 @@ private long getWaitTime(Invocation invocation) { // In invocation handlers, may call other microservices, invocation // timeout may be much longer than request timeout. - // But this is quite rare, for simplicity, default two times of request timeout. - // If users need longer timeout, can configure invocation timeout. - return invocation.getOperationMeta().getConfig().getMsRequestTimeout() * 2; + // For simplicity, default 30000 or two times of request timeout. + return Math.max(invocation.getOperationMeta().getConfig().getMsRequestTimeout() * 2, 30000); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerBootListener.java b/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerBootListener.java index 1242ad139f4..7dc291157d3 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerBootListener.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerBootListener.java @@ -21,30 +21,24 @@ import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.LinkedHashMap; -import java.util.Map; import java.util.concurrent.ExecutorService; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; -import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; import org.apache.servicecomb.foundation.common.utils.IOUtils; import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.registry.api.registry.BasePath; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.definition.DefinitionConst; import org.apache.servicecomb.swagger.SwaggerUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; -import com.netflix.config.DynamicPropertyFactory; - -import io.swagger.models.Scheme; -import io.swagger.models.Swagger; +import io.swagger.v3.oas.models.OpenAPI; public class ProducerBootListener implements BootListener { private static final Logger LOGGER = LoggerFactory.getLogger(ProducerBootListener.class); @@ -54,34 +48,36 @@ public class ProducerBootListener implements BootListener { private static final String TMP_DIR = System.getProperty("java.io.tmpdir"); + private RegistrationManager registrationManager; + + private Environment environment; + + @Autowired + public void setRegistrationManager(RegistrationManager registrationManager) { + this.registrationManager = registrationManager; + } + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + @Override public void onAfterTransport(BootEvent event) { - boolean exportToFile = DynamicPropertyFactory.getInstance() - .getBooleanProperty(DefinitionConst.SWAGGER_EXPORT_ENABLED, true).get(); - String filePath = DynamicPropertyFactory.getInstance() - .getStringProperty(DefinitionConst.SWAGGER_DIRECTORY, TMP_DIR).get() + PATTERN; + boolean exportToFile = environment.getProperty(CoreConst.SWAGGER_EXPORT_ENABLED, boolean.class, true); + String filePath = environment.getProperty(CoreConst.SWAGGER_DIRECTORY, String.class, TMP_DIR) + PATTERN; if (exportToFile) { LOGGER.info("export microservice swagger file to path {}", filePath); } // register schema to microservice; - Microservice microservice = RegistrationManager.INSTANCE.getMicroservice(); - - String swaggerSchema = "http"; - for (String endpoint : microservice.getInstance().getEndpoints()) { - if (endpoint.startsWith("rest://") && endpoint.indexOf("sslEnabled=true") > 0) { - swaggerSchema = "https"; - break; - } - } - MicroserviceMeta microserviceMeta = event.getScbEngine().getProducerMicroserviceMeta(); for (SchemaMeta schemaMeta : microserviceMeta.getSchemaMetas().values()) { - Swagger swagger = schemaMeta.getSwagger(); - swagger.addScheme(Scheme.forValue(swaggerSchema)); + OpenAPI swagger = schemaMeta.getSwagger(); String content = SwaggerUtils.swaggerToString(swagger); if (exportToFile) { - exportToFile(String.format(filePath, microservice.getServiceName(), schemaMeta.getSchemaId()), content); + exportToFile(String.format(filePath, + BootStrapProperties.readServiceName(environment), schemaMeta.getSchemaId()), content); } else { LOGGER.info("generate swagger for {}/{}/{}, swagger: {}", microserviceMeta.getAppId(), @@ -89,36 +85,10 @@ public void onAfterTransport(BootEvent event) { schemaMeta.getSchemaId(), content); } - RegistrationManager.INSTANCE.addSchema(schemaMeta.getSchemaId(), content); + this.registrationManager.addSchema(schemaMeta.getSchemaId(), content); } - - saveBasePaths(microserviceMeta); } - // just compatible to old 3rd components, servicecomb not use it...... - private void saveBasePaths(MicroserviceMeta microserviceMeta) { - if (!DynamicPropertyFactory.getInstance().getBooleanProperty(DefinitionConst.REGISTER_SERVICE_PATH, false).get()) { - return; - } - - String urlPrefix = ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX); - Map basePaths = new LinkedHashMap<>(); - for (SchemaMeta schemaMeta : microserviceMeta.getSchemaMetas().values()) { - Swagger swagger = schemaMeta.getSwagger(); - - String basePath = swagger.getBasePath(); - if (StringUtils.isNotEmpty(urlPrefix) && !basePath.startsWith(urlPrefix)) { - basePath = urlPrefix + basePath; - } - if (StringUtils.isNotEmpty(basePath)) { - BasePath basePathObj = new BasePath(); - basePathObj.setPath(basePath); - basePaths.put(basePath, basePathObj); - } - } - - RegistrationManager.INSTANCE.addBasePath(basePaths.values()); - } // bug: can not close all thread for edge @Override diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.java b/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.java index 752df947fe5..391bc6a4723 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.java @@ -23,6 +23,7 @@ import java.util.concurrent.Executor; import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.ProducerProvider; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.CoreMetaUtils; @@ -33,14 +34,13 @@ import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.engine.SwaggerProducer; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.netflix.config.DynamicPropertyFactory; - -import io.swagger.models.Swagger; +import io.swagger.v3.oas.models.OpenAPI; public class ProducerProviderManager { private static final Logger LOGGER = LoggerFactory.getLogger(ProducerProviderManager.class); @@ -94,15 +94,16 @@ public SchemaMeta registerSchema(String schemaId, Object instance) { public SchemaMeta registerSchema(String schemaId, Class schemaInterface, Object instance) { MicroserviceMeta producerMicroserviceMeta = scbEngine.getProducerMicroserviceMeta(); - Swagger swagger = scbEngine.getSwaggerLoader().loadLocalSwagger( - producerMicroserviceMeta.getAppId(), - producerMicroserviceMeta.getShortName(), - schemaId); SwaggerProducer swaggerProducer = scbEngine.getSwaggerEnvironment() - .createProducer(instance, schemaInterface, swagger); - swagger = swaggerProducer.getSwagger(); + .createProducer(instance, schemaInterface); + OpenAPI swagger = swaggerProducer.getSwagger(); registerUrlPrefixToSwagger(swagger); + // register self OpenAPI to registry + scbEngine.getOpenAPIRegistryManager() + .registerOpenAPI(BootStrapProperties.readApplication(scbEngine.getEnvironment()), + BootStrapProperties.readServiceName(scbEngine.getEnvironment()), schemaId, swagger); + SchemaMeta schemaMeta = producerMicroserviceMeta.registerSchemaMeta(schemaId, swagger); schemaMeta.putExtData(CoreMetaUtils.SWAGGER_PRODUCER, swaggerProducer); Executor reactiveExecutor = scbEngine.getExecutorManager().findExecutorById(ExecutorManager.EXECUTOR_REACTIVE); @@ -119,15 +120,14 @@ public SchemaMeta registerSchema(String schemaId, Class schemaInterface, Obje } // This is special requirement by users: When service deployed in tomcat,user want to use RestTemplate to - // call REST service by the full url. e.g. restTemplate.getForObejct("cse://serviceName/root/prefix/health") - // By default, user's do not need context prefix, e.g. restTemplate.getForObejct("cse://serviceName/health") - private void registerUrlPrefixToSwagger(Swagger swagger) { + // call REST service by the full url. e.g. restTemplate.getForObject("cse://serviceName/root/prefix/health") + // By default, user's do not need context prefix, e.g. restTemplate.getForObject("cse://serviceName/health") + private void registerUrlPrefixToSwagger(OpenAPI swagger) { String urlPrefix = ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX); - if (!StringUtils.isEmpty(urlPrefix) && !swagger.getBasePath().startsWith(urlPrefix) - && DynamicPropertyFactory.getInstance() - .getBooleanProperty(DefinitionConst.REGISTER_URL_PREFIX, false).get()) { - LOGGER.info("Add swagger base path prefix for {} with {}", swagger.getBasePath(), urlPrefix); - swagger.setBasePath(urlPrefix + swagger.getBasePath()); + if (!StringUtils.isEmpty(urlPrefix) && !SwaggerUtils.getBasePath(swagger).startsWith(urlPrefix) + && scbEngine.getEnvironment().getProperty(DefinitionConst.REGISTER_URL_PREFIX, boolean.class, false)) { + LOGGER.info("Add swagger base path prefix for {} with {}", SwaggerUtils.getBasePath(swagger), urlPrefix); + SwaggerUtils.setBasePath(swagger, urlPrefix + SwaggerUtils.getBasePath(swagger)); } } } diff --git a/core/src/main/java/org/apache/servicecomb/core/registry/ThirdServiceRegister.java b/core/src/main/java/org/apache/servicecomb/core/registry/ThirdServiceRegister.java deleted file mode 100644 index 375d3e9830f..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/registry/ThirdServiceRegister.java +++ /dev/null @@ -1,181 +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.servicecomb.core.registry; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.apache.servicecomb.core.BootListener; -import org.apache.servicecomb.core.invocation.endpoint.EndpointUtils; -import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.consumer.MicroserviceVersions; -import org.apache.servicecomb.registry.consumer.StaticMicroserviceVersions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.EnvironmentAware; -import org.springframework.core.env.Environment; -import org.springframework.util.CollectionUtils; - -/** - *
- * for declare a 3rd service as a servicecomb service
- * assume a 3rd service:
- *   1. named svc
- *   2. have 2 address: https://svc-1 and https://svc-2
- *   3. have 2 schemas: schema1 and schema2
- *
- * usage:
- *   1. define schema interface in JAX-RS or springMVC mode
- *     1) schema1
- *      {@code
- *        @Path("/v1/svc")
- *        public interface Schema1Client {
- *          @GET
- *          @Path("/add")
- *          int add(@QueryParam("q-x") int x, @QueryParam("q-y") int y);
- *
- *          @GET
- *          @Path("/minus")
- *          int minus(@QueryParam("q-x") int x, @QueryParam("q-y") int y);
- *        }
- *      }
- *     2) schema2
- *   2. add configuration to microservice.yaml
- *      {@code
- *        svc:
- *          urls:
- *            - https://svc-1
- *            - https://svc-2
- *      }
- *   3. declare the 3rd service
- *      {@code
- *        @Configuration
- *        public class Svc extends ThirdServiceRegister {
- *          public static final String NAME = "svc";
- *
- *          public Svc() {
- *            super(NAME);
- *
- *            addSchema("schema1", Schema1Client.class);
- *            addSchema("schema2", Schema2Client.class);
- *          }
- *        }
- *      }
- * 
- */ -public abstract class ThirdServiceRegister implements BootListener, EnvironmentAware { - private static final Logger LOGGER = LoggerFactory.getLogger(ThirdServiceRegister.class); - - public static final int ORDER = -1000; - - public static final String VERSION = "1.0"; - - public static final List DEFAULT_ADDRESSES = Collections.singletonList("http://127.0.0.1"); - - /** - * role: {@link EndpointUtils}
- * default to any address - */ - protected List urls = DEFAULT_ADDRESSES; - - protected String appId; - - protected final String microserviceName; - - // for 3rd service, schema interface is client interface too - protected final Map> schemaByIdMap = new HashMap<>(); - - public ThirdServiceRegister(String microserviceName) { - this.microserviceName = microserviceName; - } - - @Override - public int getOrder() { - return ORDER; - } - - public List getUrls() { - return urls; - } - - @Override - public void setEnvironment(Environment environment) { - String urlKey = microserviceName + ".urls"; - @SuppressWarnings("unchecked") - List urls = environment.getProperty(urlKey, List.class); - setUrls(urlKey, urls); - } - - public void setUrls(String urlKey, List urls) { - if (CollectionUtils.isEmpty(urls)) { - LOGGER.warn("missing configuration, key = {}", urlKey); - return; - } - - this.urls = urls; - } - - protected void addSchema(String schemaId, Class schemaCls) { - schemaByIdMap.put(schemaId, schemaCls); - } - - @Override - public void onBeforeRegistry(BootEvent event) { - appId = event.getScbEngine().getAppId(); - registerMicroserviceMapping(); - } - - protected void registerMicroserviceMapping() { - List endpoints = createEndpoints(); - List instances = createInstances(endpoints); - - DiscoveryManager.INSTANCE.getAppManager() - .getOrCreateMicroserviceManager(appId) - .getVersionsByName() - .computeIfAbsent(microserviceName, svcName -> createMicroserviceVersions(instances)); - - LOGGER.info("register third service, name={}, endpoints={}.", microserviceName, endpoints); - } - - protected List createEndpoints() { - return urls.stream() - .map(EndpointUtils::formatFromUri) - .collect(Collectors.toList()); - } - - protected List createInstances(List endpoints) { - return endpoints.stream() - .map(endpoint -> { - MicroserviceInstance instance = new MicroserviceInstance(); - instance.setEndpoints(Collections.singletonList(endpoint)); - return instance; - }) - .collect(Collectors.toList()); - } - - protected MicroserviceVersions createMicroserviceVersions(List instances) { - return new StaticMicroserviceVersions( - DiscoveryManager.INSTANCE.getAppManager(), - appId, - microserviceName) - .init(schemaByIdMap, VERSION, instances); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/registry/discovery/EndpointDiscoveryFilter.java b/core/src/main/java/org/apache/servicecomb/core/registry/discovery/EndpointDiscoveryFilter.java index 80c060e65d8..666626b0697 100644 --- a/core/src/main/java/org/apache/servicecomb/core/registry/discovery/EndpointDiscoveryFilter.java +++ b/core/src/main/java/org/apache/servicecomb/core/registry/discovery/EndpointDiscoveryFilter.java @@ -21,16 +21,27 @@ import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; import org.apache.servicecomb.registry.discovery.AbstractEndpointDiscoveryFilter; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; public class EndpointDiscoveryFilter extends AbstractEndpointDiscoveryFilter { private static final Logger LOGGER = LoggerFactory.getLogger(EndpointDiscoveryFilter.class); + protected SCBEngine scbEngine; + + public EndpointDiscoveryFilter() { + } + + @Autowired + public void setScbEngine(SCBEngine scbEngine) { + this.scbEngine = scbEngine; + } + @Override public int getOrder() { return Short.MAX_VALUE; @@ -44,8 +55,8 @@ protected String findTransportName(DiscoveryContext context, DiscoveryTreeNode p @Override protected Object createEndpoint(DiscoveryContext context, String transportName, String endpoint, - MicroserviceInstance instance) { - Transport transport = SCBEngine.getInstance().getTransportManager().findTransport(transportName); + StatefulDiscoveryInstance instance) { + Transport transport = scbEngine.getTransportManager().findTransport(transportName); if (transport == null) { LOGGER.info("not deployed transport {}, ignore {}.", transportName, endpoint); return null; diff --git a/core/src/main/java/org/apache/servicecomb/core/registry/discovery/OperationInstancesDiscoveryFilter.java b/core/src/main/java/org/apache/servicecomb/core/registry/discovery/OperationInstancesDiscoveryFilter.java deleted file mode 100644 index a61b3535693..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/registry/discovery/OperationInstancesDiscoveryFilter.java +++ /dev/null @@ -1,152 +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.servicecomb.core.registry.discovery; - -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.definition.CoreMetaUtils; -import org.apache.servicecomb.core.definition.MicroserviceMeta; -import org.apache.servicecomb.core.definition.OperationMeta; -import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; -import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.consumer.AppManager; -import org.apache.servicecomb.registry.consumer.MicroserviceVersion; -import org.apache.servicecomb.registry.consumer.MicroserviceVersions; -import org.apache.servicecomb.registry.discovery.AbstractDiscoveryFilter; -import org.apache.servicecomb.registry.discovery.DiscoveryContext; -import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; -import org.apache.servicecomb.registry.version.VersionRule; -import org.apache.servicecomb.registry.version.VersionRuleUtils; - -import com.netflix.config.DynamicPropertyFactory; - -/** - *
- *   create operation related instances
- *
- *   Preconditions:
- *     compatible is ensure by appManager.getOrCreateMicroserviceVersionRule
- *     instances in "DiscoveryTreeNode parent" are compatible:
- *       new version can only add operations, not delete operations
- *
- *   eg:
- *     microservice name is ms1
- *     2 instances are 1.0.0, instance id are i1/i2, schemaId is s1, operations are o1/o2
- *     3 instances are 1.0.1, instance id are i3/i4/i5, schemaId is s1, operations are o1/o2/o3
- *
- *     will create nodes:
- *     {
- *       "ms1.s1.o1": {
- *         {"i1": instance-i1}, {"i2": instance-i2}, {"i3": instance-i3}, {"i4": instance-i4}, {"i5": instance-i5}
- *       },
- *       "ms1.s1.o2": {
- *         {"i1": instance-i1}, {"i2": instance-i2}, {"i3": instance-i3}, {"i4": instance-i4}, {"i5": instance-i5}
- *       },
- *       "ms1.s1.o3": {
- *         {"i3": instance-i3}, {"i4": instance-i4}, {"i5": instance-i5}
- *       },
- *     }
- *
- *     that means, if invoke o1 or o2, can use 5 instances, but if invoke o3, can only use 3 instances
- *     by this filter, we can make sure that new operations will not route to old instances
- * 
- */ -public class OperationInstancesDiscoveryFilter extends AbstractDiscoveryFilter { - @Override - public int getOrder() { - return -20000; - } - - @Override - public boolean enabled() { - return DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.loadbalance.filter.operation.enabled", true).get(); - } - - @Override - public boolean isGroupingFilter() { - return true; - } - - @Override - protected String findChildName(DiscoveryContext context, DiscoveryTreeNode parent) { - Invocation invocation = context.getInputParameters(); - return invocation.getMicroserviceQualifiedName(); - } - - @Override - public void init(DiscoveryContext context, DiscoveryTreeNode parent) { - Invocation invocation = context.getInputParameters(); - // sort versions - List microserviceVersions = sortedMicroserviceVersion(invocation, parent.data()); - - Map operationNodes = new ConcurrentHashMapEx<>(); - for (MicroserviceVersion microserviceVersion : microserviceVersions) { - MicroserviceMeta microserviceMeta = CoreMetaUtils.getMicroserviceMeta(microserviceVersion); - for (OperationMeta operationMeta : microserviceMeta.getOperations()) { - DiscoveryTreeNode node = operationNodes.get(operationMeta.getMicroserviceQualifiedName()); - if (node == null) { - Map instanceMap = microserviceVersion.getInstances().stream() - .collect(Collectors.toMap(MicroserviceInstance::getInstanceId, Function.identity())); - DiscoveryTreeNode tempNode = createOperationNode(parent, microserviceVersion); - tempNode.data(instanceMap); - operationNodes.put(operationMeta.getMicroserviceQualifiedName(), tempNode); - continue; - } - - // exist, append instances - microserviceVersion.getInstances().forEach(microserviceInstance -> - node.mapData().put(microserviceInstance.getInstanceId(), microserviceInstance)); - } - } - - parent.children(operationNodes); - } - - protected List sortedMicroserviceVersion(Invocation invocation, - Map instances) { - OperationMeta latestOperationMeta = invocation.getOperationMeta(); - MicroserviceMeta latestMicroserviceMeta = latestOperationMeta.getSchemaMeta().getMicroserviceMeta(); - AppManager appManager = DiscoveryManager.INSTANCE.getAppManager(); - MicroserviceVersions microserviceVersions = - appManager.getOrCreateMicroserviceVersions(latestMicroserviceMeta.getAppId(), - latestMicroserviceMeta.getMicroserviceName()); - - Map uniqueMicroserviceVersion = new HashMap<>(); - for (MicroserviceInstance instance : instances.values()) { - MicroserviceVersion microserviceVersion = microserviceVersions.getVersion(instance.getServiceId()); - uniqueMicroserviceVersion.put(instance.getServiceId(), microserviceVersion); - } - return uniqueMicroserviceVersion.values().stream().sorted(Comparator.comparing(MicroserviceVersion::getVersion)) - .collect(Collectors.toList()); - } - - private DiscoveryTreeNode createOperationNode(DiscoveryTreeNode parent, MicroserviceVersion microserviceVersion) { - VersionRule versionRule = VersionRuleUtils.getOrCreate(microserviceVersion.getVersion().getVersion() + "+"); - return new DiscoveryTreeNode() - .subName(parent, versionRule.getVersionRule()) - .data(new HashMap<>()); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/tracing/BraveTraceIdGenerator.java b/core/src/main/java/org/apache/servicecomb/core/tracing/BraveTraceIdGenerator.java index 7a2eee0ead0..6f888c813e7 100644 --- a/core/src/main/java/org/apache/servicecomb/core/tracing/BraveTraceIdGenerator.java +++ b/core/src/main/java/org/apache/servicecomb/core/tracing/BraveTraceIdGenerator.java @@ -17,14 +17,14 @@ package org.apache.servicecomb.core.tracing; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import brave.internal.Platform; public class BraveTraceIdGenerator implements TraceIdGenerator { @Override public String getTraceIdKeyName() { - return Const.TRACE_ID_NAME; + return CoreConst.TRACE_ID_NAME; } @Override diff --git a/core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdLogger.java b/core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdLogger.java index 72fd77a3909..3819bb90218 100644 --- a/core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdLogger.java +++ b/core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdLogger.java @@ -18,18 +18,19 @@ import org.apache.servicecomb.core.Invocation; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.slf4j.MDC; import org.slf4j.Marker; public class TraceIdLogger { + private static final Logger LOGGER = LoggerFactory.getLogger("scb-trace-id"); + private static final Marker MARKER = new ScbMarker(); - private static final String KEY_TRACE_ID = "SERVICECOMB_TRACE_ID"; + public static final String KEY_TRACE_ID = "SERVICECOMB_TRACE_ID"; private final Invocation invocation; - private String name; - public TraceIdLogger(Invocation invocation) { this.invocation = invocation; } @@ -38,34 +39,36 @@ public Invocation getInvocation() { return invocation; } + public static String constructSource(String source) { + return "[" + source + "(" + + Thread.currentThread().getStackTrace()[2].getLineNumber() + ")]"; + } + public final String getName() { - if (name == null) { - name = invocation.getTraceId() + "-" + invocation.getInvocationId(); - } - return name; + return invocation.getTraceId(); } - public void error(Logger logger, String format, Object... arguments) { + public void error(String format, Object... arguments) { MDC.put(KEY_TRACE_ID, getName()); - logger.error(MARKER, format, arguments); + LOGGER.error(MARKER, format, arguments); MDC.remove(KEY_TRACE_ID); } - public void warn(Logger logger, String format, Object... arguments) { + public void warn(String format, Object... arguments) { MDC.put(KEY_TRACE_ID, getName()); - logger.warn(MARKER, format, arguments); + LOGGER.warn(MARKER, format, arguments); MDC.remove(KEY_TRACE_ID); } - public void info(Logger logger, String format, Object... arguments) { + public void info(String format, Object... arguments) { MDC.put(KEY_TRACE_ID, getName()); - logger.info(MARKER, format, arguments); + LOGGER.info(MARKER, format, arguments); MDC.remove(KEY_TRACE_ID); } - public void debug(Logger logger, String format, Object... arguments) { + public void debug(String format, Object... arguments) { MDC.put(KEY_TRACE_ID, getName()); - logger.debug(MARKER, format, arguments); + LOGGER.debug(MARKER, format, arguments); MDC.remove(KEY_TRACE_ID); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/transport/AbstractTransport.java b/core/src/main/java/org/apache/servicecomb/core/transport/AbstractTransport.java index 24278d3c92a..5c1ee3cc177 100644 --- a/core/src/main/java/org/apache/servicecomb/core/transport/AbstractTransport.java +++ b/core/src/main/java/org/apache/servicecomb/core/transport/AbstractTransport.java @@ -17,34 +17,49 @@ package org.apache.servicecomb.core.transport; +import java.net.Inet6Address; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.stream.Collectors; +import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.foundation.common.net.IpPort; import org.apache.servicecomb.foundation.common.net.NetUtils; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.foundation.vertx.SharedVertxFactory; -import org.apache.servicecomb.registry.RegistrationManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; import io.vertx.core.Vertx; public abstract class AbstractTransport implements Transport { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTransport.class); + + public static final String PUBLISH_ADDRESS = "servicecomb.service.publishAddress"; + + private static final String PUBLISH_PORT = "servicecomb.{transport_name}.publishPort"; /* * 用于参数传递:比如向RestServerVerticle传递endpoint地址。 */ public static final String ENDPOINT_KEY = "servicecomb.endpoint"; - protected Vertx transportVertx = SharedVertxFactory.getSharedVertx(); + protected Vertx transportVertx; protected Endpoint endpoint; protected Endpoint publishEndpoint; + protected Environment environment; + @Override public Endpoint getPublishEndpoint() { return publishEndpoint; @@ -55,6 +70,12 @@ public Endpoint getEndpoint() { return endpoint; } + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + this.transportVertx = SharedVertxFactory.getSharedVertx(environment); + } + protected void setListenAddressWithoutSchema(String addressWithoutSchema) { setListenAddressWithoutSchema(addressWithoutSchema, null); } @@ -69,7 +90,7 @@ protected void setListenAddressWithoutSchema(String addressWithoutSchema, this.endpoint = new Endpoint(this, NetUtils.getRealListenAddress(getName(), addressWithoutSchema)); if (this.endpoint.getEndpoint() != null) { - this.publishEndpoint = new Endpoint(this, RegistrationManager.getPublishAddress(getName(), + this.publishEndpoint = new Endpoint(this, getPublishAddress(getName(), addressWithoutSchema)); } else { this.publishEndpoint = null; @@ -88,7 +109,9 @@ private String genAddressWithoutSchema(String addressWithoutSchema, Map new BasicNameValuePair(entry.getKey(), entry.getValue())).collect(Collectors.toList()), StandardCharsets.UTF_8.name()); + String encodedQuery = URLEncodedUtils.format( + pairs.entrySet().stream().map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()), StandardCharsets.UTF_8.name()); addressWithoutSchema += encodedQuery; @@ -102,4 +125,67 @@ public Object parseAddress(String address) { } return new URIEndpointObject(address); } + + /** + * In the case that listening address configured as 0.0.0.0, the publish address will be determined + * by the query result for the net interfaces. + * + * @return the publish address, or {@code null} if the param {@code address} is null. + */ + protected String getPublishAddress(String schema, String address) { + if (address == null) { + return address; + } + + try { + URI originalURI = new URI(schema + "://" + address); + IpPort ipPort = NetUtils.parseIpPort(originalURI); + if (ipPort == null) { + LOGGER.warn("address {} not valid.", address); + return null; + } + + IpPort publishIpPort = genPublishIpPort(schema, ipPort); + URIBuilder builder = new URIBuilder(originalURI); + return builder.setHost(publishIpPort.getHostOrIp()).setPort(publishIpPort.getPort()).build().toString(); + } catch (URISyntaxException e) { + LOGGER.warn("address {} not valid.", address); + return null; + } + } + + private IpPort genPublishIpPort(String schema, IpPort ipPort) { + String publicAddressSetting = environment.getProperty(PUBLISH_ADDRESS, ""); + publicAddressSetting = publicAddressSetting.trim(); + + String publishPortKey = PUBLISH_PORT.replace("{transport_name}", schema); + int publishPortSetting = environment.getProperty(publishPortKey, int.class, 0); + int publishPort = publishPortSetting == 0 ? ipPort.getPort() : publishPortSetting; + + if (publicAddressSetting.isEmpty()) { + InetSocketAddress socketAddress = ipPort.getSocketAddress(); + if (socketAddress.getAddress().isAnyLocalAddress()) { + String host = NetUtils.getHostAddress(); + if (Inet6Address.class.isInstance(socketAddress.getAddress())) { + host = NetUtils.getIpv6HostAddress(); + } + LOGGER.warn("address {}, auto select a host address to publish {}:{}, maybe not the correct one", + socketAddress, + host, + publishPort); + return new IpPort(host, publishPort); + } + + return ipPort; + } + + if (publicAddressSetting.startsWith("{") && publicAddressSetting.endsWith("}")) { + publicAddressSetting = NetUtils + .ensureGetInterfaceAddress( + publicAddressSetting.substring(1, publicAddressSetting.length() - 1)) + .getHostAddress(); + } + + return new IpPort(publicAddressSetting, publishPort); + } } diff --git a/core/src/main/java/org/apache/servicecomb/core/transport/TransportClassAnnotationProcessor.java b/core/src/main/java/org/apache/servicecomb/core/transport/TransportClassAnnotationProcessor.java new file mode 100644 index 00000000000..5af6705abc7 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/transport/TransportClassAnnotationProcessor.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.servicecomb.core.transport; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import io.swagger.v3.oas.models.OpenAPI; + +public class TransportClassAnnotationProcessor implements ClassAnnotationProcessor { + @Override + public Type getProcessType() { + return Transport.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, Transport transport) { + OpenAPI swagger = swaggerGenerator.getOpenAPI(); + if (StringUtils.isNotEmpty(transport.name())) { + swagger.addExtension(CoreConst.TRANSPORT_NAME, transport.name()); + } + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/transport/TransportManager.java b/core/src/main/java/org/apache/servicecomb/core/transport/TransportManager.java index ca0b157b141..64ed82a5c93 100644 --- a/core/src/main/java/org/apache/servicecomb/core/transport/TransportManager.java +++ b/core/src/main/java/org/apache/servicecomb/core/transport/TransportManager.java @@ -29,9 +29,10 @@ import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.apache.servicecomb.registry.RegistrationManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; public class TransportManager { private static final Logger LOGGER = LoggerFactory.getLogger(TransportManager.class); @@ -44,6 +45,13 @@ public Map getTransportMap() { return transportMap; } + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + public void clearTransportBeforeInit() { transports.clear(); } @@ -57,6 +65,7 @@ public void addTransportsBeforeInit(List transports) { } public void init(SCBEngine scbEngine) throws Exception { + initEnvironment(); buildTransportMap(); for (Transport transport : transportMap.values()) { @@ -64,13 +73,19 @@ public void init(SCBEngine scbEngine) throws Exception { Endpoint endpoint = transport.getPublishEndpoint(); if (endpoint != null && endpoint.getEndpoint() != null) { LOGGER.info("endpoint to publish: {}", endpoint.getEndpoint()); - RegistrationManager.INSTANCE.addEndpoint(endpoint.getEndpoint()); + scbEngine.getRegistrationManager().addEndpoint(endpoint.getEndpoint()); } continue; } } } + private void initEnvironment() { + for (Transport transport : transports) { + transport.setEnvironment(environment); + } + } + protected void buildTransportMap() { Map> groups = groupByName(); diff --git a/core/src/main/java/org/apache/servicecomb/core/transport/TransportMethodAnnotationProcessor.java b/core/src/main/java/org/apache/servicecomb/core/transport/TransportMethodAnnotationProcessor.java new file mode 100644 index 00000000000..617f4b87d5e --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/transport/TransportMethodAnnotationProcessor.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.servicecomb.core.transport; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import io.swagger.v3.oas.models.Operation; + +public class TransportMethodAnnotationProcessor implements MethodAnnotationProcessor { + @Override + public Type getProcessType() { + return Transport.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Transport transport) { + Operation operation = operationGenerator.getOperation(); + if (StringUtils.isNotEmpty(transport.name())) { + operation.addExtension(CoreConst.TRANSPORT_NAME, transport.name()); + } + } +} diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.BootListener b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.BootListener deleted file mode 100644 index e6db84b3b38..00000000000 --- a/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.BootListener +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.core.provider.producer.ProducerBootListener \ No newline at end of file diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector deleted file mode 100644 index c33b0de60b1..00000000000 --- a/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector +++ /dev/null @@ -1,19 +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. -# - -org.apache.servicecomb.core.bootup.ServiceInformationCollector -org.apache.servicecomb.core.bootup.FilterChainCollector \ No newline at end of file diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider index 51f9e53fd2f..d21d7c1fe15 100644 --- a/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.core.filter.impl.DefaultFilterProvider \ No newline at end of file +org.apache.servicecomb.core.filter.impl.DefaultFilterProvider diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.tracing.TraceIdGenerator b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.tracing.TraceIdGenerator index 3a57a7db661..8e61e1f2b81 100644 --- a/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.tracing.TraceIdGenerator +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.tracing.TraceIdGenerator @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.core.tracing.BraveTraceIdGenerator \ No newline at end of file +org.apache.servicecomb.core.tracing.BraveTraceIdGenerator diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.registry.discovery.DiscoveryFilter b/core/src/main/resources/META-INF/services/org.apache.servicecomb.registry.discovery.DiscoveryFilter deleted file mode 100644 index 9ff276f9a70..00000000000 --- a/core/src/main/resources/META-INF/services/org.apache.servicecomb.registry.discovery.DiscoveryFilter +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.core.registry.discovery.OperationInstancesDiscoveryFilter diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor new file mode 100644 index 00000000000..57d906d8131 --- /dev/null +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor @@ -0,0 +1,18 @@ +# +# 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.servicecomb.core.transport.TransportClassAnnotationProcessor diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor new file mode 100644 index 00000000000..a003fcbd204 --- /dev/null +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor @@ -0,0 +1,18 @@ +# +# 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.servicecomb.core.transport.TransportMethodAnnotationProcessor diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister index 7fb23d9bc91..1356e33ecf8 100644 --- a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.core.invocation.endpoint.EndpointContextRegister \ No newline at end of file +org.apache.servicecomb.core.invocation.endpoint.EndpointContextRegister diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory index 094ac2c2d11..6eb082fc473 100644 --- a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.core.invocation.endpoint.EndpointMapperFactory \ No newline at end of file +org.apache.servicecomb.core.invocation.endpoint.EndpointMapperFactory diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter deleted file mode 100644 index f703b9026fc..00000000000 --- a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter +++ /dev/null @@ -1,19 +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. -# - -org.apache.servicecomb.core.exception.IllegalArgumentExceptionToProducerResponseConverter -org.apache.servicecomb.core.exception.ThrowableExceptionToProducerResponseConverter \ No newline at end of file diff --git a/core/src/main/resources/META-INF/spring.factories b/core/src/main/resources/META-INF/spring.factories deleted file mode 100644 index b989939ae79..00000000000 --- a/core/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,23 +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. -## --------------------------------------------------------------------------- - -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.core.filter.CoreFilterConfiguration,\ - org.apache.servicecomb.core.invocation.CoreInvocationConfiguration,\ - org.apache.servicecomb.core.governance.CoreGovernanceConfiguration,\ - org.apache.servicecomb.core.exception.CoreExceptionConfiguration,\ - org.apache.servicecomb.core.ServiceCombCoreConfiguration diff --git a/core/src/main/resources/META-INF/spring/cse.bean.xml b/core/src/main/resources/META-INF/spring/cse.bean.xml deleted file mode 100644 index 7b0757e169d..00000000000 --- a/core/src/main/resources/META-INF/spring/cse.bean.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - diff --git a/core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..74f863c2bda --- /dev/null +++ b/core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,22 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.core.filter.CoreFilterConfiguration +org.apache.servicecomb.core.invocation.CoreInvocationConfiguration +org.apache.servicecomb.core.governance.CoreGovernanceConfiguration +org.apache.servicecomb.core.exception.CoreExceptionConfiguration +org.apache.servicecomb.core.ServiceCombCoreConfiguration diff --git a/core/src/main/resources/microservice.yaml b/core/src/main/resources/microservice.yaml index 2784017e0e4..f77c5ce5601 100644 --- a/core/src/main/resources/microservice.yaml +++ b/core/src/main/resources/microservice.yaml @@ -16,8 +16,3 @@ ## --------------------------------------------------------------------------- servicecomb-config-order: -500 - -servicecomb: - exception: - invocation: - print-stack-trace: false diff --git a/core/src/test/java/org/apache/servicecomb/core/TestConfig.java b/core/src/test/java/org/apache/servicecomb/core/TestConfig.java index 02f8200d808..a4584b1e64f 100644 --- a/core/src/test/java/org/apache/servicecomb/core/TestConfig.java +++ b/core/src/test/java/org/apache/servicecomb/core/TestConfig.java @@ -20,8 +20,8 @@ import java.util.HashMap; import java.util.Map; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.Response.StatusType; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; @@ -35,11 +35,11 @@ public class TestConfig { @Test public void testConstants() { - Assertions.assertEquals("x-cse-context", Const.CSE_CONTEXT); - Assertions.assertEquals("rest", Const.RESTFUL); - Assertions.assertEquals("", Const.ANY_TRANSPORT); - Assertions.assertEquals("latest", Const.VERSION_RULE_LATEST); - Assertions.assertEquals("0.0.0.0+", Const.DEFAULT_VERSION_RULE); + Assertions.assertEquals("x-cse-context", CoreConst.CSE_CONTEXT); + Assertions.assertEquals("rest", CoreConst.RESTFUL); + Assertions.assertEquals("", CoreConst.ANY_TRANSPORT); + Assertions.assertEquals("latest", CoreConst.VERSION_RULE_LATEST); + Assertions.assertEquals("0.0.0.0+", CoreConst.DEFAULT_VERSION_RULE); } @Test diff --git a/core/src/test/java/org/apache/servicecomb/core/TestConfigurationSpringInitializer.java b/core/src/test/java/org/apache/servicecomb/core/TestConfigurationSpringInitializer.java index e663bf21f69..27b38f60110 100644 --- a/core/src/test/java/org/apache/servicecomb/core/TestConfigurationSpringInitializer.java +++ b/core/src/test/java/org/apache/servicecomb/core/TestConfigurationSpringInitializer.java @@ -16,13 +16,11 @@ */ package org.apache.servicecomb.core; -import com.netflix.config.ConfigurationManager; -import mockit.Deencapsulation; -import org.apache.commons.configuration.Configuration; +import java.util.HashMap; +import java.util.Map; + import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -34,48 +32,19 @@ import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertySource; -import org.springframework.core.env.StandardEnvironment; -import org.springframework.core.env.SystemEnvironmentPropertySource; import org.springframework.jndi.JndiPropertySource; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - public class TestConfigurationSpringInitializer { @BeforeEach public void beforeTest() { Configurator.setRootLevel(Level.OFF); - ConfigUtil.clearExtraConfig(); - ArchaiusUtils.resetConfig(); - Configurator.setRootLevel(Level.INFO); } @AfterEach public void afterTest() { - ConfigUtil.clearExtraConfig(); - ArchaiusUtils.resetConfig(); - } - @Test - public void testAll() { - ConfigurationSpringInitializer configurationSpringInitializer = new ConfigurationSpringInitializer(); - ConfigUtil.installDynamicConfig(); - - Object o = ConfigUtil.getProperty("zq"); - @SuppressWarnings("unchecked") - List> listO = (List>) o; - Assertions.assertEquals(3, listO.size()); - Assertions.assertNull(ConfigUtil.getProperty("notExist")); - - Configuration instance = ConfigurationManager.getConfigInstance(); - ConfigUtil.installDynamicConfig(); - // must not reinstall - Assertions.assertEquals(instance, ConfigurationManager.getConfigInstance()); } @Test @@ -146,18 +115,6 @@ public void testSetEnvironment() { } return value; }).when(environment).getProperty(ArgumentMatchers.anyString(), ArgumentMatchers.eq(Object.class)); - - new ConfigurationSpringInitializer().setEnvironment(environment); - - Map> extraLocalConfig = getExtraConfigMapFromConfigUtil(); - Assertions.assertFalse(extraLocalConfig.isEmpty()); - Map extraProperties = extraLocalConfig - .get(ConfigurationSpringInitializer.EXTRA_CONFIG_SOURCE_PREFIX + environment.getClass().getName() + "@" - + environment.hashCode()); - Assertions.assertNotNull(extraLocalConfig); - for (Entry entry : propertyMap.entrySet()) { - Assertions.assertEquals(entry.getValue(), extraProperties.get(entry.getKey())); - } } @Test @@ -191,54 +148,5 @@ public void testSetEnvironmentOnEnvironmentName() { propertySources2.addFirst(new MapPropertySource("mapPropertySource2", map2)); Mockito.when(environment2.getProperty("key2", Object.class)).thenReturn("value2"); Mockito.when(environment2.getProperty("key2")).thenReturn("value2"); - - ConfigurationSpringInitializer configurationSpringInitializer = new ConfigurationSpringInitializer(); - configurationSpringInitializer.setEnvironment(environment0); - configurationSpringInitializer.setEnvironment(environment1); - configurationSpringInitializer.setEnvironment(environment2); - - Map> extraConfig = getExtraConfigMapFromConfigUtil(); - Assertions.assertEquals(3, extraConfig.size()); - - Map extraProperties = extraConfig - .get(ConfigurationSpringInitializer.EXTRA_CONFIG_SOURCE_PREFIX + "application"); - Assertions.assertEquals(1, extraProperties.size()); - Assertions.assertEquals("application", extraProperties.get("spring.config.name")); - - extraProperties = extraConfig.get(ConfigurationSpringInitializer.EXTRA_CONFIG_SOURCE_PREFIX + "bootstrap"); - Assertions.assertEquals(1, extraProperties.size()); - Assertions.assertEquals("bootstrap", extraProperties.get("spring.application.name")); - - extraProperties = extraConfig.get( - ConfigurationSpringInitializer.EXTRA_CONFIG_SOURCE_PREFIX + environment2.getClass().getName() + "@" - + environment2.hashCode()); - Assertions.assertEquals(1, extraProperties.size()); - Assertions.assertEquals("value2", extraProperties.get("key2")); - } - - @Test - public void should_throw_exception_when_given_ignoreResolveFailure_false() { - Assertions.assertThrows(RuntimeException.class, () -> { - StandardEnvironment environment = newStandardEnvironment(); - - ConfigurationSpringInitializer configurationSpringInitializer = new ConfigurationSpringInitializer(); - configurationSpringInitializer.setEnvironment(environment); - }); - } - - private Map> getExtraConfigMapFromConfigUtil() { - return Deencapsulation - .getField(ConfigUtil.class, "EXTRA_CONFIG_MAP"); - } - - private StandardEnvironment newStandardEnvironment() { - Map envProperties = new HashMap<>(); - envProperties.put("IFS-X", "${IFS-X}"); - PropertySource> systemEnvironmentPropertySource = new SystemEnvironmentPropertySource("system-env", envProperties); - - StandardEnvironment environment = new StandardEnvironment(); - environment.getPropertySources() - .addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, systemEnvironmentPropertySource); - return environment; } } diff --git a/core/src/test/java/org/apache/servicecomb/core/TestEndpoint.java b/core/src/test/java/org/apache/servicecomb/core/TestEndpoint.java index 9650bc309da..17e1ef0c5e1 100644 --- a/core/src/test/java/org/apache/servicecomb/core/TestEndpoint.java +++ b/core/src/test/java/org/apache/servicecomb/core/TestEndpoint.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.core; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -38,7 +38,7 @@ public void testEndpoint() { @Test public void testEndpointAddressConstructor() { Transport transport = Mockito.mock(Transport.class); - MicroserviceInstance instance = Mockito.mock(MicroserviceInstance.class); + StatefulDiscoveryInstance instance = Mockito.mock(StatefulDiscoveryInstance.class); Endpoint endpoint = new Endpoint(transport, "rest://123.6.6.6:8080", instance, "iot://123.6.6.6:8080"); Assertions.assertEquals(endpoint.getAddress(), "iot://123.6.6.6:8080"); Assertions.assertEquals(endpoint.getEndpoint(), "rest://123.6.6.6:8080"); diff --git a/core/src/test/java/org/apache/servicecomb/core/TestException.java b/core/src/test/java/org/apache/servicecomb/core/TestException.java index be6dc4519ba..58bce6bce43 100644 --- a/core/src/test/java/org/apache/servicecomb/core/TestException.java +++ b/core/src/test/java/org/apache/servicecomb/core/TestException.java @@ -25,32 +25,32 @@ public class TestException { @Test public void testCseException() { - CseException oExeception = new CseException("500", "InternalServerError"); - Assertions.assertEquals("500", oExeception.getCode()); + CseException oException = new CseException("500", "InternalServerError"); + Assertions.assertEquals("500", oException.getCode()); Assertions.assertEquals("ServiceDefinitionException Code:500, Message:InternalServerError", - oExeception.toString()); + oException.toString()); - oExeception = new CseException("503", "OwnException", new Throwable()); - Assertions.assertEquals("503", oExeception.getCode()); + oException = new CseException("503", "OwnException", new Throwable()); + Assertions.assertEquals("503", oException.getCode()); } @Test public void testExceptionUtils() { - CseException oExeception = ExceptionUtils + CseException oException = ExceptionUtils .createCseException("servicecomb.handler.ref.not.exist", new String("test")); - Assertions.assertEquals("servicecomb.handler.ref.not.exist", oExeception.getCode()); + Assertions.assertEquals("servicecomb.handler.ref.not.exist", oException.getCode()); - oExeception = + oException = ExceptionUtils.createCseException("servicecomb.handler.ref.not.exist", new Throwable(), new String("test")); - Assertions.assertEquals("servicecomb.handler.ref.not.exist", oExeception.getCode()); + Assertions.assertEquals("servicecomb.handler.ref.not.exist", oException.getCode()); - oExeception = ExceptionUtils.producerOperationNotExist("servicecomb.error", "unit-testing"); - Assertions.assertEquals("servicecomb.producer.operation.not.exist", oExeception.getCode()); + oException = ExceptionUtils.producerOperationNotExist("servicecomb.error", "unit-testing"); + Assertions.assertEquals("servicecomb.producer.operation.not.exist", oException.getCode()); - oExeception = ExceptionUtils.handlerRefNotExist("servicecomb.double.error"); - Assertions.assertEquals("servicecomb.handler.ref.not.exist", oExeception.getCode()); + oException = ExceptionUtils.handlerRefNotExist("servicecomb.double.error"); + Assertions.assertEquals("servicecomb.handler.ref.not.exist", oException.getCode()); - oExeception = ExceptionUtils.lbAddressNotFound("microServiceName", "my rule my world", "transportChannel"); - Assertions.assertEquals("servicecomb.lb.no.available.address", oExeception.getCode()); + oException = ExceptionUtils.lbAddressNotFound("microServiceName", "my rule my world", "transportChannel"); + Assertions.assertEquals("servicecomb.lb.no.available.address", oException.getCode()); } } diff --git a/core/src/test/java/org/apache/servicecomb/core/TestInvocation.java b/core/src/test/java/org/apache/servicecomb/core/TestInvocation.java index 814c7038b02..80b6bbda461 100644 --- a/core/src/test/java/org/apache/servicecomb/core/TestInvocation.java +++ b/core/src/test/java/org/apache/servicecomb/core/TestInvocation.java @@ -17,12 +17,10 @@ package org.apache.servicecomb.core; import java.util.Arrays; -import java.util.Map; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.event.InvocationBaseEvent; -import org.apache.servicecomb.core.event.InvocationBusinessMethodFinishEvent; import org.apache.servicecomb.core.event.InvocationBusinessMethodStartEvent; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; @@ -39,6 +37,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.jupiter.api.Assertions; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; @@ -47,10 +46,8 @@ import mockit.Mock; import mockit.MockUp; import mockit.Mocked; -import org.junit.jupiter.api.Assertions; public class TestInvocation { - Invocation invocation; @Mocked Endpoint endpoint; @@ -61,9 +58,6 @@ public class TestInvocation { @Mocked InvocationRuntimeType invocationRuntimeType; - @Mocked - Map arguments; - static long nanoTime = 123; @BeforeClass @@ -87,8 +81,6 @@ public static void classTeardown() { @Test public void onStart() { - mockNonaTime(); - Holder result = new Holder<>(); Object subscriber = new Object() { @Subscribe @@ -98,25 +90,13 @@ public void onStart(InvocationStartEvent event) { }; EventManager.register(subscriber); - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); - invocation.onStart(nanoTime); + Invocation invocation = new Invocation(endpoint, operationMeta, null); + invocation.onStart(); Assertions.assertSame(invocation, result.value); - Assertions.assertEquals(nanoTime, invocation.getInvocationStageTrace().getStart()); - EventManager.unregister(subscriber); } - @Test - public void onStartExecute() { - mockNonaTime(); - - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); - invocation.onExecuteStart(); - - Assertions.assertEquals(nanoTime, invocation.getInvocationStageTrace().getStartExecution()); - } - @Test public void onFinish() { mockNonaTime(); @@ -130,12 +110,11 @@ public void onStart(InvocationFinishEvent event) { }; EventManager.register(subscriber); - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); + Invocation invocation = new Invocation(endpoint, operationMeta, null); Assertions.assertFalse(invocation.isFinished()); Response response = Response.succResp(null); invocation.onFinish(response); - Assertions.assertEquals(nanoTime, result.value.getNanoCurrent()); Assertions.assertSame(invocation, result.value.getInvocation()); Assertions.assertSame(response, result.value.getResponse()); Assertions.assertTrue(invocation.isFinished()); @@ -150,19 +129,19 @@ public void onStart(InvocationFinishEvent event) { @Test public void isConsumer_yes() { - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); + Invocation invocation = new Invocation(endpoint, operationMeta, null); Assertions.assertFalse(invocation.isConsumer()); } @Test public void isConsumer_no(@Mocked ReferenceConfig referenceConfig) { - Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, arguments); + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); Assertions.assertTrue(invocation.isConsumer()); } @Test public void localContext(@Mocked ReferenceConfig referenceConfig) { - Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, arguments); + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); invocation.addLocalContext("k", 1); Assertions.assertSame(invocation.getHandlerContext(), invocation.getLocalContext()); @@ -171,13 +150,13 @@ public void localContext(@Mocked ReferenceConfig referenceConfig) { @Test public void traceId_fromContext(@Mocked ReferenceConfig referenceConfig) { - Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, arguments); - invocation.addContext(Const.TRACE_ID_NAME, "abc"); + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); + invocation.addContext(CoreConst.TRACE_ID_NAME, "abc"); - invocation.onStart(0); + invocation.onStart(); Assertions.assertEquals("abc", invocation.getTraceId()); - Assertions.assertEquals("abc", invocation.getTraceId(Const.TRACE_ID_NAME)); + Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); } @Test @@ -189,32 +168,33 @@ public void traceId_consumerCreateTraceId(@Mocked ReferenceConfig referenceConfi result = "abc"; } }; - Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, arguments); + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); - invocation.onStart(0); + invocation.onStart(); Assertions.assertEquals("abc", invocation.getTraceId()); - Assertions.assertEquals("abc", invocation.getTraceId(Const.TRACE_ID_NAME)); + Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); } @Test public void traceId_fromRequest(@Mocked Endpoint endpoint, @Mocked HttpServletRequestEx requestEx) { new Expectations() { { - requestEx.getHeader(Const.TRACE_ID_NAME); + requestEx.getHeader(CoreConst.TRACE_ID_NAME); result = "abc"; } }; - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); + Invocation invocation = new Invocation(endpoint, operationMeta, null); - invocation.onStart(requestEx, 0); + invocation.onStart(requestEx); Assertions.assertEquals("abc", invocation.getTraceId()); - Assertions.assertEquals("abc", invocation.getTraceId(Const.TRACE_ID_NAME)); + Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); } @Test public void traceId_producerCreateTraceId(@Mocked Endpoint endpoint, @Mocked HttpServletRequestEx requestEx) { + TraceIdGenerator generator = SPIServiceUtils.getTargetService(TraceIdGenerator.class, BraveTraceIdGenerator.class); new Expectations(generator) { { @@ -222,12 +202,12 @@ public void traceId_producerCreateTraceId(@Mocked Endpoint endpoint, @Mocked Htt result = "abc"; } }; - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); + Invocation invocation = new Invocation(endpoint, operationMeta, null); - invocation.onStart(requestEx, 0); + invocation.onStart(requestEx); Assertions.assertEquals("abc", invocation.getTraceId()); - Assertions.assertEquals("abc", invocation.getTraceId(Const.TRACE_ID_NAME)); + Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); } @Test @@ -258,7 +238,7 @@ public void traceIdGeneratorInit(@Mocked TraceIdGenerator gen1, @Mocked TraceIdG InvocationBaseEvent invocationBaseEvent; @Test - public void onBusinessMethodStart() { + public void test_business_execute_time_correct() { Object listener = new Object() { @Subscribe public void onBusinessMethodStart(InvocationBusinessMethodStartEvent event) { @@ -266,66 +246,29 @@ public void onBusinessMethodStart(InvocationBusinessMethodStartEvent event) { } }; EventManager.getEventBus().register(listener); - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); + Invocation invocation = new Invocation(endpoint, operationMeta, null); mockNonaTime(); invocation.onBusinessMethodStart(); EventManager.getEventBus().unregister(listener); Assertions.assertSame(invocation, invocationBaseEvent.getInvocation()); - Assertions.assertEquals(nanoTime, invocation.getInvocationStageTrace().getStartBusinessMethod()); - } - - @Test - public void onBusinessMethodFinish() { - Object listener = new Object() { - @Subscribe - public void onBusinessMethodStart(InvocationBusinessMethodFinishEvent event) { - invocationBaseEvent = event; - } - }; - EventManager.getEventBus().register(listener); - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); - invocation.onBusinessMethodFinish(); - EventManager.getEventBus().unregister(listener); - - Assertions.assertSame(invocation, invocationBaseEvent.getInvocation()); - } - - @Test - public void onBusinessFinish() { - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); - mockNonaTime(); + nanoTime++; invocation.onBusinessFinish(); - - Assertions.assertEquals(nanoTime, invocation.getInvocationStageTrace().getFinishBusiness()); + Assertions.assertEquals(1, invocation.getInvocationStageTrace().calcBusinessExecute()); } @Test public void marker(@Mocked ReferenceConfig referenceConfig) { Invocation.INVOCATION_ID.set(0); - Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, arguments); - invocation.addContext(Const.TRACE_ID_NAME, "abc"); - invocation.onStart(0); - Assertions.assertEquals("abc-0", invocation.getTraceIdLogger().getName()); - - invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, arguments); - invocation.addContext(Const.TRACE_ID_NAME, "abc"); - invocation.onStart(0); - Assertions.assertEquals("abc-1", invocation.getTraceIdLogger().getName()); - } - - @Test - public void isThirdPartyInvocation(@Mocked ReferenceConfig referenceConfig) { - Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, arguments); - Assertions.assertFalse(invocation.isThirdPartyInvocation()); + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); + invocation.addContext(CoreConst.TRACE_ID_NAME, "abc"); + invocation.onStart(); + Assertions.assertEquals("abc", invocation.getTraceIdLogger().getName()); - new Expectations() { - { - referenceConfig.is3rdPartyService(); - result = true; - } - }; - Assertions.assertTrue(invocation.isThirdPartyInvocation()); + invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); + invocation.addContext(CoreConst.TRACE_ID_NAME, "abc"); + invocation.onStart(); + Assertions.assertEquals("abc", invocation.getTraceIdLogger().getName()); } } diff --git a/core/src/test/java/org/apache/servicecomb/core/TestSCBApplicationListener.java b/core/src/test/java/org/apache/servicecomb/core/TestSCBApplicationListener.java index bca38c93953..99690c3c310 100644 --- a/core/src/test/java/org/apache/servicecomb/core/TestSCBApplicationListener.java +++ b/core/src/test/java/org/apache/servicecomb/core/TestSCBApplicationListener.java @@ -16,36 +16,38 @@ */ package org.apache.servicecomb.core; -import org.apache.servicecomb.config.ConfigUtil; +import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; +import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; + import org.apache.servicecomb.core.bootstrap.SCBBootstrap; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.apache.servicecomb.registry.DiscoveryManager; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.context.event.ContextClosedEvent; +import org.springframework.core.env.Environment; public class TestSCBApplicationListener { @BeforeEach public void before() { - ConfigUtil.installDynamicConfig(); } @AfterAll public static void classTeardown() { - DiscoveryManager.renewInstance(); - ArchaiusUtils.resetConfig(); } @Test public void onApplicationEvent_close() { ContextClosedEvent contextClosedEvent = Mockito.mock(ContextClosedEvent.class); - SCBEngine scbEngine = SCBBootstrap.createSCBEngineForTest(); + Environment environment = Mockito.mock(Environment.class); + SCBEngine scbEngine = SCBBootstrap.createSCBEngineForTest(environment); + scbEngine.setEnvironment(environment); + Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); scbEngine.setStatus(SCBStatus.UP); - SCBApplicationListener listener = new SCBApplicationListener(); + SCBApplicationListener listener = new SCBApplicationListener(scbEngine); listener.onApplicationEvent(contextClosedEvent); Assertions.assertEquals(SCBStatus.DOWN, scbEngine.getStatus()); diff --git a/core/src/test/java/org/apache/servicecomb/core/TestTransport.java b/core/src/test/java/org/apache/servicecomb/core/TestTransport.java index e97f8088317..4ab2f99ff80 100644 --- a/core/src/test/java/org/apache/servicecomb/core/TestTransport.java +++ b/core/src/test/java/org/apache/servicecomb/core/TestTransport.java @@ -17,11 +17,11 @@ package org.apache.servicecomb.core; -import org.apache.servicecomb.swagger.invocation.AsyncResponse; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.springframework.core.env.Environment; public class TestTransport { @BeforeAll @@ -31,21 +31,20 @@ public static void classSetup() { @AfterAll public static void classTeardown() { - SCBEngine.getInstance().destroy(); } @Test public void testEndpoint() throws Exception { Endpoint oEndpoint = new Endpoint(new Transport() { - @Override - public void send(Invocation invocation, AsyncResponse asyncResp) { - } - @Override public Object parseAddress(String address) { return "127.0.0.1"; } + @Override + public void setEnvironment(Environment environment) { + + } @Override public boolean init() { return true; diff --git a/core/src/test/java/org/apache/servicecomb/core/definition/OperationConfigTest.java b/core/src/test/java/org/apache/servicecomb/core/definition/OperationConfigTest.java index 3a2ae4d1cf5..ad664261980 100644 --- a/core/src/test/java/org/apache/servicecomb/core/definition/OperationConfigTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/definition/OperationConfigTest.java @@ -21,7 +21,7 @@ import java.util.concurrent.TimeUnit; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -37,7 +37,7 @@ void should_get_rest_value() { long nano = TimeUnit.MILLISECONDS.toNanos(2); assertThat(config.getNanoRestRequestWaitInPoolTimeout()).isEqualTo(nano); - assertThat(config.getNanoRequestWaitInPoolTimeout(Const.RESTFUL)).isEqualTo(nano); + assertThat(config.getNanoRequestWaitInPoolTimeout(CoreConst.RESTFUL)).isEqualTo(nano); } @Test @@ -47,7 +47,7 @@ void should_get_highway_value() { long nano = TimeUnit.MILLISECONDS.toNanos(2); assertThat(config.getNanoHighwayRequestWaitInPoolTimeout()).isEqualTo(nano); - assertThat(config.getNanoRequestWaitInPoolTimeout(Const.HIGHWAY)).isEqualTo(nano); + assertThat(config.getNanoRequestWaitInPoolTimeout(CoreConst.HIGHWAY)).isEqualTo(nano); } @Test diff --git a/core/src/test/java/org/apache/servicecomb/core/event/TestInvocationFinishEvent.java b/core/src/test/java/org/apache/servicecomb/core/event/TestInvocationFinishEvent.java deleted file mode 100644 index 63380368d4a..00000000000 --- a/core/src/test/java/org/apache/servicecomb/core/event/TestInvocationFinishEvent.java +++ /dev/null @@ -1,57 +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.servicecomb.core.event; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.invocation.InvocationStageTrace; -import org.apache.servicecomb.swagger.invocation.Response; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; -import org.junit.jupiter.api.Assertions; - -public class TestInvocationFinishEvent { - InvocationFinishEvent event; - - @Test - public void construct(@Mocked Invocation invocation, @Mocked Response response) { - InvocationStageTrace stageTrace = new InvocationStageTrace(invocation); - long time = 123; - new MockUp() { - @Mock - long nanoTime() { - return time; - } - }; - new Expectations() { - { - invocation.getInvocationStageTrace(); - result = stageTrace; - } - }; - stageTrace.finish(); - - event = new InvocationFinishEvent(invocation, response); - - Assertions.assertEquals(time, event.getNanoCurrent()); - Assertions.assertSame(invocation, event.getInvocation()); - Assertions.assertSame(response, event.getResponse()); - } -} diff --git a/core/src/test/java/org/apache/servicecomb/core/exception/ExceptionsTest.java b/core/src/test/java/org/apache/servicecomb/core/exception/ExceptionsTest.java index 16f458c0876..0594084e30c 100644 --- a/core/src/test/java/org/apache/servicecomb/core/exception/ExceptionsTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/exception/ExceptionsTest.java @@ -17,15 +17,13 @@ package org.apache.servicecomb.core.exception; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; -import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; +import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static org.assertj.core.api.Assertions.assertThat; +import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Collections; -import javax.annotation.Nullable; -import javax.ws.rs.core.Response.StatusType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; @@ -34,8 +32,12 @@ import org.junit.jupiter.api.Test; import io.vertx.core.json.Json; +import jakarta.ws.rs.core.Response.StatusType; class ExceptionsTest { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + @Test void should_not_convert_invocation_exception() { InvocationException exception = Exceptions.genericConsumer("msg"); @@ -53,8 +55,13 @@ void should_convert_unknown_client_exception_to_invocation_exception() { assertThat(invocationException).hasCause(exception); assertThat(invocationException.getStatus()).isEqualTo(BAD_REQUEST); assertThat(invocationException.getErrorData()).isInstanceOf(CommonExceptionData.class); - assertThat(Json.encode(invocationException.getErrorData())) - .isEqualTo("{\"code\":\"SCB.00000000\",\"message\":\"msg\"}"); + + try { + assertThat(objectMapper.readTree(Json.encode(invocationException.getErrorData()))) + .isEqualTo(objectMapper.readTree("{\"code\":\"SCB.00000000\",\"message\":\"Unexpected exception when processing none. msg\"}")); + } catch (Exception e) { + throw new AssertionError("Error during JSON comparison: " + e.getMessage(), e); + } } @Test @@ -66,8 +73,13 @@ void should_convert_unknown_server_exception_to_invocation_exception() { assertThat(invocationException).hasCause(exception); assertThat(invocationException.getStatus()).isEqualTo(INTERNAL_SERVER_ERROR); assertThat(invocationException.getErrorData()).isInstanceOf(CommonExceptionData.class); - assertThat(Json.encode(invocationException.getErrorData())) - .isEqualTo("{\"code\":\"SCB.50000000\",\"message\":\"msg\"}"); + + try { + assertThat(objectMapper.readTree(Json.encode(invocationException.getErrorData()))) + .isEqualTo(objectMapper.readTree("{\"code\":\"SCB.50000000\",\"message\":\"Unexpected exception when processing none. msg\"}")); + } catch (Exception e) { + throw new AssertionError("Error during JSON comparison: " + e.getMessage(), e); + } } static class ThrowExceptionWhenConvert implements ExceptionConverter { @@ -82,7 +94,7 @@ public boolean canConvert(Throwable throwable) { } @Override - public InvocationException convert(@Nullable Invocation invocation, Throwable throwable, StatusType genericStatus) { + public InvocationException convert(Invocation invocation, Throwable throwable, StatusType genericStatus) { throw new RuntimeExceptionWithoutStackTrace("mock exception when convert"); } } diff --git a/core/src/test/java/org/apache/servicecomb/core/exception/converter/TimeoutExceptionConverterTest.java b/core/src/test/java/org/apache/servicecomb/core/exception/converter/TimeoutExceptionConverterTest.java index 001dd8263cb..3a1b69908bb 100644 --- a/core/src/test/java/org/apache/servicecomb/core/exception/converter/TimeoutExceptionConverterTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/exception/converter/TimeoutExceptionConverterTest.java @@ -21,7 +21,7 @@ import java.util.concurrent.TimeoutException; -import javax.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; diff --git a/core/src/test/java/org/apache/servicecomb/core/executor/TestExecutorManager.java b/core/src/test/java/org/apache/servicecomb/core/executor/TestExecutorManager.java index cb1db767539..2fc689efb05 100644 --- a/core/src/test/java/org/apache/servicecomb/core/executor/TestExecutorManager.java +++ b/core/src/test/java/org/apache/servicecomb/core/executor/TestExecutorManager.java @@ -21,27 +21,31 @@ import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; import mockit.Expectations; import mockit.Mocked; -import org.junit.jupiter.api.Assertions; public class TestExecutorManager { @Mocked Executor defaultExecutor; + ExecutorManager executorManager = new ExecutorManager(); + + Environment environment = Mockito.mock(Environment.class); + @Before public void setup() { - ArchaiusUtils.resetConfig(); + executorManager.setEnvironment(environment); } @After public void teardown() { - ArchaiusUtils.resetConfig(); } @Test @@ -53,7 +57,7 @@ public void findExecutor_oneParam(@Mocked Executor executor, @Mocked OperationMe } }; - Assertions.assertSame(executor, new ExecutorManager().findExecutor(operationMeta)); + Assertions.assertSame(executor, executorManager.findExecutor(operationMeta)); } @Test @@ -62,7 +66,8 @@ public void findExecutor_twoParam_opCfg_withoutOpDef(@Mocked Executor executor, // String schemaQualifiedName = "schemaId.opId"; String microserviceQualifiedName = "microserviceName.schemaId.opId"; String opBeanId = "opBeanId"; - ArchaiusUtils.setProperty(ExecutorManager.KEY_EXECUTORS_PREFIX + microserviceQualifiedName, opBeanId); + Mockito.when(environment.getProperty( + ExecutorManager.KEY_EXECUTORS_PREFIX + microserviceQualifiedName)).thenReturn(opBeanId); new Expectations(BeanUtils.class) { { operationMeta.getMicroserviceQualifiedName(); @@ -72,7 +77,7 @@ public void findExecutor_twoParam_opCfg_withoutOpDef(@Mocked Executor executor, } }; - Assertions.assertSame(executor, new ExecutorManager().findExecutor(operationMeta, null)); + Assertions.assertSame(executor, executorManager.findExecutor(operationMeta, null)); } @Test @@ -81,7 +86,8 @@ public void findExecutor_twoParam_opCfg_withOpDef(@Mocked Executor executor, @Mocked OperationMeta operationMeta) { String microserviceQualifiedName = "microserviceName.schemaId.opId"; String opBeanId = "opBeanId"; - ArchaiusUtils.setProperty(ExecutorManager.KEY_EXECUTORS_PREFIX + microserviceQualifiedName, opBeanId); + Mockito.when(environment.getProperty( + ExecutorManager.KEY_EXECUTORS_PREFIX + microserviceQualifiedName)).thenReturn(opBeanId); new Expectations(BeanUtils.class) { { operationMeta.getMicroserviceQualifiedName(); @@ -91,13 +97,13 @@ public void findExecutor_twoParam_opCfg_withOpDef(@Mocked Executor executor, } }; - Assertions.assertSame(executor, new ExecutorManager().findExecutor(operationMeta, defExecutor)); + Assertions.assertSame(executor, executorManager.findExecutor(operationMeta, defExecutor)); } @Test public void findExecutor_twoParam_schemaCfg_withOpDef(@Mocked OperationMeta operationMeta, @Mocked Executor defExecutor) { - Assertions.assertSame(defExecutor, new ExecutorManager().findExecutor(operationMeta, defExecutor)); + Assertions.assertSame(defExecutor, executorManager.findExecutor(operationMeta, defExecutor)); } @Test @@ -106,7 +112,8 @@ public void findExecutor_twoParam_schemaCfg_withoutOpDef(@Mocked Executor execut String microserviceName = "serviceName"; String schemaName = "schemaId"; String opBeanId = "opBeanId"; - ArchaiusUtils.setProperty(ExecutorManager.KEY_EXECUTORS_PREFIX + microserviceName + "." + schemaName, opBeanId); + Mockito.when(environment.getProperty(ExecutorManager.KEY_EXECUTORS_PREFIX + microserviceName + "." + schemaName)) + .thenReturn(opBeanId); new Expectations(BeanUtils.class) { { operationMeta.getSchemaId(); @@ -117,7 +124,7 @@ public void findExecutor_twoParam_schemaCfg_withoutOpDef(@Mocked Executor execut result = executor; } }; - Assertions.assertSame(executor, new ExecutorManager().findExecutor(operationMeta, null)); + Assertions.assertSame(executor, executorManager.findExecutor(operationMeta, null)); } @Test @@ -125,7 +132,7 @@ public void findExecutor_twoParam_defaultCfg(@Mocked Executor executor, @Mocked SchemaMeta schemaMeta, @Mocked OperationMeta operationMeta) { String beanId = "beanId"; - ArchaiusUtils.setProperty(ExecutorManager.KEY_EXECUTORS_DEFAULT, beanId); + Mockito.when(environment.getProperty(ExecutorManager.KEY_EXECUTORS_DEFAULT)).thenReturn(beanId); new Expectations(BeanUtils.class) { { BeanUtils.getBean(beanId); @@ -133,6 +140,6 @@ public void findExecutor_twoParam_defaultCfg(@Mocked Executor executor, } }; - Assertions.assertSame(executor, new ExecutorManager().findExecutor(operationMeta, null)); + Assertions.assertSame(executor, executorManager.findExecutor(operationMeta, null)); } } diff --git a/core/src/test/java/org/apache/servicecomb/core/executor/TestGroupExecutor.java b/core/src/test/java/org/apache/servicecomb/core/executor/TestGroupExecutor.java index aae5bdeb59e..dd95105992b 100644 --- a/core/src/test/java/org/apache/servicecomb/core/executor/TestGroupExecutor.java +++ b/core/src/test/java/org/apache/servicecomb/core/executor/TestGroupExecutor.java @@ -17,40 +17,55 @@ package org.apache.servicecomb.core.executor; +import static org.apache.servicecomb.core.executor.GroupExecutor.KEY_CORE_THREADS; +import static org.apache.servicecomb.core.executor.GroupExecutor.KEY_GROUP; +import static org.apache.servicecomb.core.executor.GroupExecutor.KEY_MAX_IDLE_SECOND; +import static org.apache.servicecomb.core.executor.GroupExecutor.KEY_MAX_QUEUE_SIZE; +import static org.apache.servicecomb.core.executor.GroupExecutor.KEY_MAX_THREADS; +import static org.apache.servicecomb.core.executor.GroupExecutor.KEY_OLD_MAX_THREAD; + import java.util.List; import java.util.Map; import java.util.concurrent.Executor; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; - -import mockit.Deencapsulation; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import mockit.Deencapsulation; public class TestGroupExecutor { String strThreadTest = "default"; @BeforeEach public void setup() { - ArchaiusUtils.resetConfig(); + Mockito.when(environment.getProperty(KEY_GROUP, int.class, 2)).thenReturn(2); + Mockito.when(environment.getProperty(KEY_CORE_THREADS, int.class, 25)).thenReturn(25); + Mockito.when(environment.getProperty(KEY_MAX_IDLE_SECOND, int.class, 60)).thenReturn(60); + Mockito.when(environment.getProperty(KEY_MAX_THREADS, int.class, -1)).thenReturn(-1); + Mockito.when(environment.getProperty(KEY_OLD_MAX_THREAD, int.class, -1)).thenReturn(-1); + Mockito.when(environment.getProperty(KEY_MAX_QUEUE_SIZE, int.class, Integer.MAX_VALUE)) + .thenReturn(Integer.MAX_VALUE); } @AfterAll public static void teardown() { - ArchaiusUtils.resetConfig(); } - GroupExecutor groupExecutor = new GroupExecutor(); + Environment environment = Mockito.mock(Environment.class); + + GroupExecutor groupExecutor = new GroupExecutor(environment); @Test public void groupCount() { groupExecutor.initConfig(); Assertions.assertEquals(2, groupExecutor.groupCount); - ArchaiusUtils.setProperty(GroupExecutor.KEY_GROUP, 4); + Mockito.when(environment.getProperty(KEY_GROUP, int.class, 2)).thenReturn(4); groupExecutor.initConfig(); Assertions.assertEquals(4, groupExecutor.groupCount); } @@ -60,7 +75,7 @@ public void coreThreads() { groupExecutor.initConfig(); Assertions.assertEquals(25, groupExecutor.coreThreads); - ArchaiusUtils.setProperty(GroupExecutor.KEY_CORE_THREADS, 100); + Mockito.when(environment.getProperty(KEY_CORE_THREADS, int.class, 25)).thenReturn(100); groupExecutor.initConfig(); Assertions.assertEquals(100, groupExecutor.coreThreads); } @@ -70,7 +85,7 @@ public void maxIdleInSecond() { groupExecutor.initConfig(); Assertions.assertEquals(60, groupExecutor.maxIdleInSecond); - ArchaiusUtils.setProperty(GroupExecutor.KEY_MAX_IDLE_SECOND, 100); + Mockito.when(environment.getProperty(KEY_MAX_IDLE_SECOND, int.class, 60)).thenReturn(100); groupExecutor.initConfig(); Assertions.assertEquals(100, groupExecutor.maxIdleInSecond); } @@ -79,8 +94,7 @@ public void maxIdleInSecond() { public void maxQueueSize() { groupExecutor.initConfig(); Assertions.assertEquals(Integer.MAX_VALUE, groupExecutor.maxQueueSize); - - ArchaiusUtils.setProperty(GroupExecutor.KEY_MAX_QUEUE_SIZE, 100); + Mockito.when(environment.getProperty(KEY_MAX_QUEUE_SIZE, int.class, Integer.MAX_VALUE)).thenReturn(100); groupExecutor.initConfig(); Assertions.assertEquals(100, groupExecutor.maxQueueSize); } @@ -91,30 +105,29 @@ public void maxThreads() { Assertions.assertEquals(100, groupExecutor.maxThreads); LogCollector collector = new LogCollector(); - ArchaiusUtils.setProperty(GroupExecutor.KEY_OLD_MAX_THREAD, 200); + Mockito.when(environment.getProperty(KEY_OLD_MAX_THREAD, int.class, -1)).thenReturn(200); groupExecutor.initConfig(); Assertions.assertEquals(200, groupExecutor.maxThreads); Assertions.assertEquals( "servicecomb.executor.default.thread-per-group is deprecated, recommended to use servicecomb.executor.default.maxThreads-per-group.", collector.getEvents().get(collector.getEvents().size() - 2).getMessage().getFormattedMessage()); - collector.teardown(); + collector.tearDown(); - ArchaiusUtils.setProperty(GroupExecutor.KEY_MAX_THREADS, 300); + Mockito.when(environment.getProperty(KEY_MAX_THREADS, int.class, -1)).thenReturn(300); groupExecutor.initConfig(); Assertions.assertEquals(300, groupExecutor.maxThreads); } @Test public void adjustCoreThreads() { - ArchaiusUtils.setProperty(GroupExecutor.KEY_MAX_THREADS, 10); - + Mockito.when(environment.getProperty(KEY_MAX_THREADS, int.class, -1)).thenReturn(10); LogCollector collector = new LogCollector(); groupExecutor.initConfig(); Assertions.assertEquals(10, groupExecutor.maxThreads); Assertions.assertEquals( "coreThreads is bigger than maxThreads, change from 25 to 10.", collector.getEvents().get(collector.getEvents().size() - 2).getMessage().getFormattedMessage()); - collector.teardown(); + collector.tearDown(); } @Test diff --git a/core/src/test/java/org/apache/servicecomb/core/filter/FilterChainTest.java b/core/src/test/java/org/apache/servicecomb/core/filter/FilterChainTest.java index 0903cc03c53..5a5dc1cb05e 100644 --- a/core/src/test/java/org/apache/servicecomb/core/filter/FilterChainTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/filter/FilterChainTest.java @@ -21,12 +21,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; -import java.io.IOException; -import java.time.Duration; import java.util.List; import java.util.Vector; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -58,19 +55,35 @@ public class FilterChainTest { List msg = new Vector<>(); - Filter recordThreadFilter = (invocation, nextNode) -> { - msg.add(Thread.currentThread().getName()); - if (nextNode == null) { - return CompletableFuture.completedFuture(Response.ok(null)); + Filter recordThreadFilter = new AbstractFilter() { + @Override + public String getName() { + return "f2"; } - return nextNode.onFilter(invocation); + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + msg.add(Thread.currentThread().getName()); + if (nextNode == null) { + return CompletableFuture.completedFuture(Response.ok(null)); + } + + return nextNode.onFilter(invocation); + } }; Filter scheduler = new ScheduleFilter(); - Filter exceptionFilter = (invocation, nextNode) -> { - throw new IllegalStateException("e1"); + Filter exceptionFilter = new AbstractFilter() { + @Override + public String getName() { + return "f1"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + throw new IllegalStateException("e1"); + } }; @BeforeClass @@ -133,20 +146,4 @@ public void should_stop_chain_when_middle_filter_throw_exception() { .isInstanceOf(IllegalStateException.class) .hasMessage("e1"); } - - @Test - public void should_support_retry_logic() { - Filter exceptionFilter = (invocation, nextNode) -> { - throw new CompletionException(new IOException("net error")); - }; - SimpleRetryFilter retryFilter = new SimpleRetryFilter().setMaxRetry(3); - - CompletableFuture future = buildChain(retryFilter, recordThreadFilter, exceptionFilter) - .onFilter(invocation); - assertThat(future) - .failsWithin(Duration.ofSeconds(1)) - .withThrowableOfType(ExecutionException.class) - .withCauseExactlyInstanceOf(IOException.class) - .withMessage("java.io.IOException: net error"); - } } diff --git a/core/src/test/java/org/apache/servicecomb/core/filter/SimpleRetryFilter.java b/core/src/test/java/org/apache/servicecomb/core/filter/SimpleRetryFilter.java deleted file mode 100644 index 059277847c7..00000000000 --- a/core/src/test/java/org/apache/servicecomb/core/filter/SimpleRetryFilter.java +++ /dev/null @@ -1,102 +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.servicecomb.core.filter; - -import java.io.IOException; -import java.util.concurrent.CompletableFuture; - -import javax.annotation.Nonnull; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.exception.Exceptions; -import org.apache.servicecomb.swagger.invocation.InvocationType; -import org.apache.servicecomb.swagger.invocation.Response; - -public class SimpleRetryFilter implements ConsumerFilter { - protected int maxRetry = 3; - - @Nonnull - @Override - public String getName() { - return "simple-retry"; - } - - @Override - public int getOrder(InvocationType invocationType, String microservice) { - return Filter.CONSUMER_LOAD_BALANCE_ORDER - 900; - } - - public SimpleRetryFilter setMaxRetry(int maxRetry) { - this.maxRetry = maxRetry; - return this; - } - - @Override - public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - return new RetrySession(invocation, nextNode).run(); - } - - protected Throwable unwrapException(Throwable throwable) { - return Exceptions.unwrapIncludeInvocationException(throwable); - } - - protected boolean isRetryException(Throwable throwable) { - return !(throwable instanceof IOException); - } - - class RetrySession { - Invocation invocation; - - FilterNode nextNode; - - int retryCount; - - CompletableFuture future = new CompletableFuture<>(); - - RetrySession(Invocation invocation, FilterNode nextNode) { - this.invocation = invocation; - this.nextNode = nextNode; - } - - CompletableFuture run() { - nextNode.onFilter(invocation) - .whenComplete(this::whenNextComplete); - return future; - } - - private void whenNextComplete(Response response, Throwable throwable) { - if (throwable == null) { - future.complete(response); - return; - } - - Throwable unwrapped = unwrapException(throwable); - if (isRetryException(unwrapped)) { - future.completeExceptionally(throwable); - return; - } - - retryCount++; - if (retryCount >= maxRetry) { - future.completeExceptionally(throwable); - return; - } - - run(); - } - } -} diff --git a/core/src/test/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilterTest.java b/core/src/test/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilterTest.java index 7942a355f67..4afed2d7dde 100644 --- a/core/src/test/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilterTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilterTest.java @@ -17,36 +17,39 @@ package org.apache.servicecomb.core.filter.impl; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static org.apache.servicecomb.core.exception.ExceptionCodes.DEFAULT_VALIDATE; import static org.apache.servicecomb.core.exception.converter.ConstraintViolationExceptionConverter.KEY_CODE; +import static org.apache.servicecomb.core.filter.impl.ParameterValidatorFilter.ENABLE_EL; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import java.util.List; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; - import org.apache.commons.lang3.reflect.MethodUtils; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.core.exception.converter.ConstraintViolationExceptionConverter; import org.apache.servicecomb.core.exception.converter.ValidateDetail; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MutablePropertySources; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import mockit.Expectations; import mockit.Mocked; @@ -86,7 +89,7 @@ public void op(@NotNull(message = "not null") String query, @Valid Model model) } } - static ParameterValidatorFilter filter = new ParameterValidatorFilter(); + ParameterValidatorFilter filter = new ParameterValidatorFilter(); @Mocked Invocation invocation; @@ -94,13 +97,19 @@ public void op(@NotNull(message = "not null") String query, @Valid Model model) @Mocked SwaggerProducerOperation operation; - @BeforeClass - public static void beforeClass() throws Exception { - filter.afterPropertiesSet(); - } + ConfigurableEnvironment environment; @Before public void setUp() throws Exception { + environment = Mockito.mock(ConfigurableEnvironment.class); + SCBEngine engine = SCBBootstrap.createSCBEngineForTest(environment); + MutablePropertySources sources = new MutablePropertySources(); + + Mockito.when(environment.getProperty(ENABLE_EL, boolean.class, false)).thenReturn(false); + Mockito.when(environment.getPropertySources()).thenReturn(sources); + filter.setEnvironment(environment); + filter.afterPropertiesSet(); + engine.setEnvironment(environment); new Expectations() { { operation.getProducerInstance(); @@ -134,7 +143,7 @@ public void status_code_should_be_bad_request() { @Test public void error_code_and_message_should_be_build_in_value() { - ArchaiusUtils.setProperty(KEY_CODE, null); + Mockito.when(environment.getProperty(KEY_CODE, String.class, DEFAULT_VALIDATE)).thenReturn(DEFAULT_VALIDATE); CommonExceptionData errorData = getExceptionData(); assertThat(errorData.getCode()).isEqualTo(DEFAULT_VALIDATE); @@ -143,13 +152,11 @@ public void error_code_and_message_should_be_build_in_value() { @Test public void should_allow_customize_error_code_by_configuration() { - ArchaiusUtils.setProperty(KEY_CODE, "test.0001"); - SPIServiceUtils.getTargetService(ExceptionConverter.class, ConstraintViolationExceptionConverter.class) - .refreshCode(); + Mockito.when(environment.getProperty(KEY_CODE, String.class, DEFAULT_VALIDATE)).thenReturn("test.0001"); + SPIServiceUtils.getTargetService(ExceptionConverter.class, ConstraintViolationExceptionConverter.class); CommonExceptionData errorData = getExceptionData(); assertThat(errorData.getCode()).isEqualTo("test.0001"); - ArchaiusUtils.setProperty(KEY_CODE, null); } @Test diff --git a/core/src/test/java/org/apache/servicecomb/core/filter/impl/ProducerOperationFilterTest.java b/core/src/test/java/org/apache/servicecomb/core/filter/impl/ProducerOperationFilterTest.java index 58a6ea0d103..dd46b17d28b 100644 --- a/core/src/test/java/org/apache/servicecomb/core/filter/impl/ProducerOperationFilterTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/filter/impl/ProducerOperationFilterTest.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.core.filter.impl; -import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; +import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; @@ -44,7 +44,7 @@ import mockit.Verifications; public class ProducerOperationFilterTest { - ProducerOperationFilter filter = new ProducerOperationFilter(); + ProviderOperationFilter filter = new ProviderOperationFilter(); @Injectable Invocation invocation; @@ -83,9 +83,6 @@ public void should_record_invocation_trace_time() throws NoSuchMethodException { invocation.onBusinessMethodStart(); times = 1; - invocation.onBusinessMethodFinish(); - times = 1; - invocation.onBusinessFinish(); times = 1; } @@ -139,11 +136,11 @@ public void should_catch_sync_business_exception() throws NoSuchMethodException CompletableFuture future = filter.onFilter(invocation, FilterNode.EMPTY); assertThat(future) - .failsWithin(Duration.ofSeconds(1)) - .withThrowableOfType(ExecutionException.class) - .withCauseExactlyInstanceOf(RuntimeExceptionWithoutStackTrace.class) - .withMessage("org.apache.servicecomb.foundation.test.scaffolding.exception" + - ".RuntimeExceptionWithoutStackTrace: syncException"); + .failsWithin(Duration.ofSeconds(1)) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(RuntimeExceptionWithoutStackTrace.class) + .withMessage("org.apache.servicecomb.foundation.test.scaffolding.exception" + + ".RuntimeExceptionWithoutStackTrace: syncException"); } @Test @@ -171,11 +168,11 @@ public void should_catch_async_business_exception() throws NoSuchMethodException CompletableFuture future = filter.onFilter(invocation, FilterNode.EMPTY); assertThat(future) - .failsWithin(Duration.ofSeconds(1)) - .withThrowableOfType(ExecutionException.class) - .withCauseExactlyInstanceOf(RuntimeExceptionWithoutStackTrace.class) - .withMessage("org.apache.servicecomb.foundation.test.scaffolding" + - ".exception.RuntimeExceptionWithoutStackTrace: asyncException"); + .failsWithin(Duration.ofSeconds(1)) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(RuntimeExceptionWithoutStackTrace.class) + .withMessage("org.apache.servicecomb.foundation.test.scaffolding" + + ".exception.RuntimeExceptionWithoutStackTrace: asyncException"); } @Test @@ -190,10 +187,9 @@ public void should_unify_IllegalArgumentException_message_when_convert_exception CompletableFuture future = filter.onFilter(invocation, FilterNode.EMPTY); assertThat(future) - .failsWithin(Duration.ofSeconds(1)) - .withThrowableOfType(ExecutionException.class) - .withCauseExactlyInstanceOf(IllegalArgumentException.class) - .withMessage("java.lang.IllegalArgumentException: wrong number of arguments"); + .failsWithin(Duration.ofSeconds(1)) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(IllegalArgumentException.class); InvocationException throwable = Exceptions .convert(invocation, catchThrowable(future::get), INTERNAL_SERVER_ERROR); diff --git a/core/src/test/java/org/apache/servicecomb/core/filter/impl/SimpleLoadBalanceFilterTest.java b/core/src/test/java/org/apache/servicecomb/core/filter/impl/SimpleLoadBalanceFilterTest.java deleted file mode 100644 index 08c1eda3f8d..00000000000 --- a/core/src/test/java/org/apache/servicecomb/core/filter/impl/SimpleLoadBalanceFilterTest.java +++ /dev/null @@ -1,76 +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.servicecomb.core.filter.impl; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import org.apache.servicecomb.core.Endpoint; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.registry.discovery.DiscoveryContext; -import org.apache.servicecomb.swagger.invocation.Response; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Injectable; -import mockit.Mocked; -import mockit.Verifications; - -public class SimpleLoadBalanceFilterTest { - SimpleLoadBalanceFilter filter = new SimpleLoadBalanceFilter(); - - @Injectable - Invocation invocation; - - @Injectable - Endpoint endpoint; - - @Mocked - DiscoveryContext discoveryContext; - - @Injectable - FilterNode nextNode; - - @Test - public void should_invoke_next_directly_when_invocation_already_has_endpoint() - throws ExecutionException, InterruptedException { - Response response = Response.ok("ok"); - new Expectations() { - { - invocation.getEndpoint(); - result = endpoint; - - nextNode.onFilter(invocation); - result = CompletableFuture.completedFuture(response); - } - }; - - Response result = filter.onFilter(invocation, nextNode).get(); - - assertThat(result).isSameAs(response); - new Verifications() { - { - discoveryContext.setInputParameters(invocation); - times = 0; - } - }; - } -} diff --git a/core/src/test/java/org/apache/servicecomb/core/handler/impl/ErrorToProducerResponseConverter.java b/core/src/test/java/org/apache/servicecomb/core/handler/impl/ErrorToProducerResponseConverter.java deleted file mode 100644 index 373d7d82282..00000000000 --- a/core/src/test/java/org/apache/servicecomb/core/handler/impl/ErrorToProducerResponseConverter.java +++ /dev/null @@ -1,35 +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.servicecomb.core.handler.impl; - -import javax.ws.rs.core.Response.Status; - -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; -import org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter; - -public class ErrorToProducerResponseConverter implements ExceptionToProducerResponseConverter { - @Override - public Class getExceptionClass() { - return Error.class; - } - - @Override - public Response convert(SwaggerInvocation swaggerInvocation, Error e) { - return Response.create(Status.OK, "response from error: " + e.getMessage()); - } -} diff --git a/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java b/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java index 1cc4b4db84a..e2e3b2c421d 100644 --- a/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java @@ -20,9 +20,12 @@ import static java.util.concurrent.CompletableFuture.completedFuture; import static org.assertj.core.api.Assertions.assertThat; +import java.util.concurrent.CompletableFuture; + import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.exception.Exceptions; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; @@ -47,7 +50,7 @@ protected Invocation sendCreateInvocationException(Throwable throwable) { } @Override - protected void sendResponse(Invocation invocation, Response response) { + protected void endResponse(Invocation invocation, Response response) { sendInvocation = invocation; } } @@ -79,7 +82,7 @@ public void should_send_exception_response_when_failed_to_create_invocation() { private void mockFilterChain() { new Expectations() { { - microserviceMeta.getFilterChain(); + microserviceMeta.getProviderFilterChain(); result = filterNode; } }; @@ -94,7 +97,7 @@ public void should_start_invocation_when_succeed_to_create_invocation() { new Verifications() { { - invocation.onStart((HttpServletRequestEx) any, anyLong); + invocation.onStart((HttpServletRequestEx) any); times = 1; } }; @@ -126,8 +129,16 @@ public void should_finish_invocation_when_invocation_success() { } private void mockInvocationFailed() { - filterNode = new FilterNode((_invocation, _node) -> { - throw new RuntimeExceptionWithoutStackTrace(); + filterNode = new FilterNode(new AbstractFilter() { + @Override + public String getName() { + return "test"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + throw new RuntimeExceptionWithoutStackTrace(); + } }); mockFilterChain(); } diff --git a/core/src/test/java/org/apache/servicecomb/core/invocation/TestInvocationStageTrace.java b/core/src/test/java/org/apache/servicecomb/core/invocation/TestInvocationStageTrace.java index 5d81bfe06ca..5b9c7040caa 100644 --- a/core/src/test/java/org/apache/servicecomb/core/invocation/TestInvocationStageTrace.java +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/TestInvocationStageTrace.java @@ -26,11 +26,11 @@ import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.jupiter.api.Assertions; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; -import org.junit.jupiter.api.Assertions; public class TestInvocationStageTrace { Invocation invocation; @@ -68,51 +68,34 @@ public void consumer() { invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, args); stageTrace = new InvocationStageTrace(invocation); - stageTrace.start(1); + stageTrace.startCreateInvocation(1); nanoTime = 2; - stageTrace.startHandlersRequest(); + stageTrace.finishCreateInvocation(); + stageTrace.startConsumerConnection(); nanoTime = 3; - stageTrace.startClientFiltersRequest(); + stageTrace.finishConsumerConnection(); + stageTrace.startConsumerEncodeRequest(); nanoTime = 4; - stageTrace.startGetConnection(); - stageTrace.startSend(); + stageTrace.finishConsumerEncodeRequest(); + stageTrace.startConsumerSendRequest(); nanoTime = 5; - stageTrace.finishGetConnection(); - stageTrace.finishWriteToBuffer(6); + stageTrace.finishConsumerSendRequest(); + stageTrace.startWaitResponse(); + nanoTime = 6; + stageTrace.finishWaitResponse(); + stageTrace.startConsumerDecodeResponse(); nanoTime = 7; - stageTrace.finishReceiveResponse(); - nanoTime = 8; - stageTrace.startClientFiltersResponse(); - nanoTime = 9; - stageTrace.finishClientFiltersResponse(); - nanoTime = 10; - stageTrace.finishHandlersResponse(); - nanoTime = 11; + stageTrace.finishConsumerDecodeResponse(); + stageTrace.finish(); - Assertions.assertEquals(1, stageTrace.getStart()); - Assertions.assertEquals(2, stageTrace.getStartHandlersRequest()); - Assertions.assertEquals(3, stageTrace.getStartClientFiltersRequest()); - Assertions.assertEquals(4, stageTrace.getStartSend()); - Assertions.assertEquals(5, stageTrace.getFinishGetConnection()); - Assertions.assertEquals(6, stageTrace.getFinishWriteToBuffer()); - Assertions.assertEquals(7, stageTrace.getFinishReceiveResponse()); - Assertions.assertEquals(8, stageTrace.getStartClientFiltersResponse()); - Assertions.assertEquals(9, stageTrace.getFinishClientFiltersResponse()); - Assertions.assertEquals(10, stageTrace.getFinishHandlersResponse()); - Assertions.assertEquals(11, stageTrace.getFinish()); - - Assertions.assertEquals(1f, stageTrace.calcInvocationPrepareTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcHandlersRequestTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcClientFiltersRequestTime(), 0.1f); - Assertions.assertEquals(2f, stageTrace.calcSendRequestTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcGetConnectionTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcWriteToBufferTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcReceiveResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcWakeConsumer(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcClientFiltersResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcHandlersResponseTime(), 0.1f); - Assertions.assertEquals(10f, stageTrace.calcTotalTime(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcPrepare(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConnection(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConsumerEncodeRequest(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConsumerSendRequest(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcWait(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConsumerDecodeResponse(), 0.1f); + Assertions.assertEquals(6f, stageTrace.calcTotal(), 0.1f); } @Test @@ -120,116 +103,81 @@ public void producer() { invocation = new Invocation(endpoint, operationMeta, args); stageTrace = new InvocationStageTrace(invocation); - stageTrace.start(1); + stageTrace.startCreateInvocation(1); nanoTime = 2; - stageTrace.startSchedule(); + stageTrace.finishCreateInvocation(); + stageTrace.startProviderDecodeRequest(); nanoTime = 3; - stageTrace.startExecution(); + stageTrace.finishProviderDecodeRequest(); + stageTrace.startProviderQueue(); nanoTime = 4; - stageTrace.startServerFiltersRequest(); + stageTrace.finishProviderQueue(); + stageTrace.startBusinessExecute(); nanoTime = 5; - stageTrace.startHandlersRequest(); + stageTrace.finishBusinessExecute(); + stageTrace.startProviderEncodeResponse(); nanoTime = 6; - stageTrace.startBusinessMethod(); + stageTrace.finishProviderEncodeResponse(); + stageTrace.startProviderSendResponse(); nanoTime = 7; - stageTrace.finishBusiness(); - nanoTime = 8; - stageTrace.finishHandlersResponse(); - nanoTime = 9; - stageTrace.finishServerFiltersResponse(); - nanoTime = 10; + stageTrace.finishProviderSendResponse(); + stageTrace.finish(); - Assertions.assertEquals(1, stageTrace.getStart()); - Assertions.assertEquals(2, stageTrace.getStartSchedule()); - Assertions.assertEquals(3, stageTrace.getStartExecution()); - Assertions.assertEquals(4, stageTrace.getStartServerFiltersRequest()); - Assertions.assertEquals(5, stageTrace.getStartHandlersRequest()); - Assertions.assertEquals(6, stageTrace.getStartBusinessMethod()); - Assertions.assertEquals(7, stageTrace.getFinishBusiness()); - Assertions.assertEquals(8, stageTrace.getFinishHandlersResponse()); - Assertions.assertEquals(9, stageTrace.getFinishServerFiltersResponse()); - Assertions.assertEquals(10, stageTrace.getFinish()); - - Assertions.assertEquals(1f, stageTrace.calcInvocationPrepareTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcThreadPoolQueueTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcServerFiltersRequestTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcHandlersRequestTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcBusinessTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcHandlersResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcServerFiltersResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcSendResponseTime(), 0.1f); - Assertions.assertEquals(9f, stageTrace.calcTotalTime(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcPrepare(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcQueue(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcProviderDecodeRequest(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcProviderEncodeResponse(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcBusinessExecute(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcProviderSendResponse(), 0.1f); + Assertions.assertEquals(6f, stageTrace.calcTotal(), 0.1f); } @Test public void edge() { invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, args); stageTrace = new InvocationStageTrace(invocation); - invocation.setEdge(true); + invocation.setEdge(); - stageTrace.start(1); + stageTrace.startCreateInvocation(1); nanoTime = 2; - stageTrace.startSchedule(); + stageTrace.finishCreateInvocation(); + stageTrace.startProviderDecodeRequest(); nanoTime = 3; - stageTrace.startExecution(); + stageTrace.finishProviderDecodeRequest(); + stageTrace.startConsumerConnection(); nanoTime = 4; - stageTrace.startServerFiltersRequest(); + stageTrace.finishConsumerConnection(); + stageTrace.startConsumerEncodeRequest(); nanoTime = 5; - stageTrace.startHandlersRequest(); + stageTrace.finishConsumerEncodeRequest(); + stageTrace.startConsumerSendRequest(); nanoTime = 6; - stageTrace.startClientFiltersRequest(); + stageTrace.finishConsumerSendRequest(); + stageTrace.startWaitResponse(); nanoTime = 7; - stageTrace.startGetConnection(); - stageTrace.startSend(); + stageTrace.finishWaitResponse(); + stageTrace.startConsumerDecodeResponse(); nanoTime = 8; - stageTrace.finishGetConnection(); - stageTrace.finishWriteToBuffer(9); + stageTrace.finishConsumerDecodeResponse(); + stageTrace.startProviderEncodeResponse(); nanoTime = 10; - stageTrace.finishReceiveResponse(); + stageTrace.finishProviderEncodeResponse(); + stageTrace.startProviderSendResponse(); nanoTime = 11; - stageTrace.startClientFiltersResponse(); - nanoTime = 12; - stageTrace.finishClientFiltersResponse(); - nanoTime = 13; - stageTrace.finishHandlersResponse(); - nanoTime = 14; - stageTrace.finishServerFiltersResponse(); - nanoTime = 15; + stageTrace.finishProviderSendResponse(); stageTrace.finish(); - Assertions.assertEquals(1, stageTrace.getStart()); - Assertions.assertEquals(2, stageTrace.getStartSchedule()); - Assertions.assertEquals(3, stageTrace.getStartExecution()); - Assertions.assertEquals(4, stageTrace.getStartServerFiltersRequest()); - Assertions.assertEquals(5, stageTrace.getStartHandlersRequest()); - Assertions.assertEquals(6, stageTrace.getStartClientFiltersRequest()); - Assertions.assertEquals(7, stageTrace.getStartSend()); - Assertions.assertEquals(8, stageTrace.getFinishGetConnection()); - Assertions.assertEquals(9, stageTrace.getFinishWriteToBuffer()); - Assertions.assertEquals(10, stageTrace.getFinishReceiveResponse()); - Assertions.assertEquals(11, stageTrace.getStartClientFiltersResponse()); - Assertions.assertEquals(12, stageTrace.getFinishClientFiltersResponse()); - Assertions.assertEquals(13, stageTrace.getFinishHandlersResponse()); - Assertions.assertEquals(14, stageTrace.getFinishServerFiltersResponse()); - Assertions.assertEquals(15, stageTrace.getFinish()); - - Assertions.assertEquals(1f, stageTrace.calcInvocationPrepareTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcThreadPoolQueueTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcServerFiltersRequestTime(), 0.1f); - - Assertions.assertEquals(1f, stageTrace.calcHandlersRequestTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcClientFiltersRequestTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcGetConnectionTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcWriteToBufferTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcReceiveResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcWakeConsumer(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcClientFiltersResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcHandlersResponseTime(), 0.1f); - - Assertions.assertEquals(1f, stageTrace.calcServerFiltersResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcSendResponseTime(), 0.1f); - - Assertions.assertEquals(14f, stageTrace.calcTotalTime(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcPrepare(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConnection(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConsumerEncodeRequest(), 0.1f); + + Assertions.assertEquals(1f, stageTrace.calcConsumerSendRequest(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcWait(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConsumerDecodeResponse(), 0.1f); + Assertions.assertEquals(2f, stageTrace.calcProviderEncodeResponse(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcProviderSendResponse(), 0.1f); + + Assertions.assertEquals(10f, stageTrace.calcTotal(), 0.1f); } } diff --git a/core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointTest.java b/core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointTest.java index d6203589c18..b3c0733bb48 100644 --- a/core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointTest.java @@ -26,7 +26,6 @@ import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.foundation.common.Holder; -import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; @@ -34,7 +33,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import io.swagger.models.Swagger; +import io.swagger.v3.oas.models.OpenAPI; public class EndpointTest { public interface TestSchema { @@ -44,30 +43,12 @@ public interface TestSchema { @Test void should_ignore_endpoint_when_generate_swagger() { SwaggerGenerator generator = SwaggerGenerator.create(TestSchema.class); - generator.getSwaggerGeneratorFeature() - .setExtJavaInterfaceInVendor(false) - .setExtJavaClassInVendor(false); - Swagger swagger = generator.generate(); + OpenAPI swagger = generator.generate(); - assertThat(SwaggerUtils.swaggerToString(swagger)) - .isEqualTo("---\n" - + "swagger: \"2.0\"\n" - + "info:\n" - + " version: \"1.0.0\"\n" - + " title: \"swagger definition for org.apache.servicecomb.core.invocation.endpoint.EndpointTest$TestSchema\"\n" - + "basePath: \"/TestSchema\"\n" - + "consumes:\n" - + "- \"application/json\"\n" - + "produces:\n" - + "- \"application/json\"\n" - + "paths:\n" - + " /say:\n" - + " post:\n" - + " operationId: \"say\"\n" - + " parameters: []\n" - + " responses:\n" - + " \"200\":\n" - + " description: \"response of 200\"\n"); + assertThat(swagger.getInfo().getTitle()).contains("EndpointTest$TestSchema"); + assertThat(swagger.getPaths()).containsKey("/say"); + assertThat(swagger.getPaths().get("/say").getPost().getOperationId()).isEqualTo("say"); + assertThat(swagger.getPaths().get("/say").getPost().getResponses()).containsKey("200"); } @Test diff --git a/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategyTest.java b/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategyTest.java index 9481fd19e38..58de7e9d2a0 100644 --- a/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategyTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategyTest.java @@ -32,17 +32,6 @@ class PassingTimeStrategyTest { PassingTimeStrategy strategy = new PassingTimeStrategy(); - @Test - void should_init_when_start_as_first_chain_node() { - Invocation invocation = new Invocation(); - invocation.getInvocationStageTrace().setStartTimeMillis(10); - - strategy.start(invocation); - - assertThat(invocation.getContext(CHAIN_START_TIME)).isEqualTo("10"); - assertThat(invocation.getLocalContext(CHAIN_START_TIME)).isEqualTo(10L); - } - @Test void should_init_when_start_as_first_node_of_a_process_but_not_first_of_a_chain() { Invocation invocation = new Invocation(); diff --git a/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategyTest.java b/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategyTest.java index 07d84e5b3e4..05d744e15db 100644 --- a/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategyTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategyTest.java @@ -31,17 +31,6 @@ class ProcessingTimeStrategyTest { ProcessingTimeStrategy strategy = new ProcessingTimeStrategy(); - @Test - void should_init_when_start_as_first_chain_node() { - Invocation invocation = new Invocation(); - invocation.getInvocationStageTrace().start(10); - - strategy.start(invocation); - - assertThat(invocation.getLocalContext(CHAIN_START_TIME)).isEqualTo(10L); - assertThat(invocation.getLocalContext(CHAIN_PROCESSING)).isEqualTo(0L); - } - @Test void should_do_nothing_when_not_first_node_of_a_process() { Invocation invocation = new Invocation(); diff --git a/core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.java b/core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.java index 9eba671f305..3b3e56092c3 100644 --- a/core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.java +++ b/core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.java @@ -17,16 +17,11 @@ package org.apache.servicecomb.core.provider.consumer; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class TestInvokerUtils { @Test public void testRetryInvocation503() { - InvocationException root = new InvocationException(503, "Service Unavailable", "Error"); - boolean canRetry = InvokerUtils.canRetryForStatusCode(Response.failResp(root)); - Assertions.assertTrue(canRetry); + } } diff --git a/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerBootListener.java b/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerBootListener.java index edecc74eff2..e327759eff2 100644 --- a/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerBootListener.java +++ b/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerBootListener.java @@ -17,26 +17,18 @@ package org.apache.servicecomb.core.provider.producer; -import java.io.Closeable; import java.util.Arrays; import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.BootListener.BootEvent; import org.apache.servicecomb.core.BootListener.EventType; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; -import org.apache.servicecomb.core.executor.GroupExecutor; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; public class TestProducerBootListener { ProducerBootListener producerBootListener = new ProducerBootListener(); @@ -51,57 +43,15 @@ public void onBootEvent_notClose() { } @Test - public void onBootEvent_close(@Mocked SCBEngine scbEngine, @Mocked MicroserviceMeta microserviceMeta, - @Mocked OperationMeta op1, - @Mocked OperationMeta op2, @Mocked GroupExecutor closeable) { - AtomicInteger count = new AtomicInteger(); - ExecutorService executorService = new MockUp() { - @Mock - void shutdown() { - count.incrementAndGet(); - } - }.getMockInstance(); - new MockUp(closeable) { - @Mock - void close() { - count.incrementAndGet(); - } - }; - new Expectations() { - { - scbEngine.getProducerMicroserviceMeta(); - result = microserviceMeta; - microserviceMeta.getOperations(); - result = Arrays.asList(op1, op2); - op1.getExecutor(); - result = executorService; - op2.getExecutor(); - result = closeable; - } - }; - BootEvent event = new BootEvent(); - event.setScbEngine(scbEngine); - event.setEventType(EventType.AFTER_CLOSE); - - producerBootListener.onBootEvent(event); + public void onBootEvent_close_unknown() { + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); + OperationMeta op = Mockito.mock(OperationMeta.class); - Assertions.assertEquals(2, count.get()); - } - - @Test - public void onBootEvent_close_unknown(@Mocked SCBEngine scbEngine, @Mocked MicroserviceMeta microserviceMeta, - @Mocked OperationMeta op1) { Executor executor = new UnCloseableExecutor(); - new Expectations() { - { - scbEngine.getProducerMicroserviceMeta(); - result = microserviceMeta; - microserviceMeta.getOperations(); - result = Arrays.asList(op1); - op1.getExecutor(); - result = executor; - } - }; + Mockito.when(scbEngine.getProducerMicroserviceMeta()).thenReturn(microserviceMeta); + Mockito.when(microserviceMeta.getOperations()).thenReturn(Arrays.asList(op)); + Mockito.when(op.getExecutor()).thenReturn(executor); try (LogCollector logCollector = new LogCollector()) { BootEvent event = new BootEvent(); diff --git a/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerProviderManager.java b/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerProviderManager.java index 6025b3a9b1c..f36229b0eea 100644 --- a/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerProviderManager.java +++ b/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerProviderManager.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.core.provider.producer; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class TestProducerProviderManager { @Test diff --git a/core/src/test/java/org/apache/servicecomb/core/registry/discovery/TestEndpointDiscoveryFilter.java b/core/src/test/java/org/apache/servicecomb/core/registry/discovery/TestEndpointDiscoveryFilter.java index 30a8bef2240..cc79ba2f684 100644 --- a/core/src/test/java/org/apache/servicecomb/core/registry/discovery/TestEndpointDiscoveryFilter.java +++ b/core/src/test/java/org/apache/servicecomb/core/registry/discovery/TestEndpointDiscoveryFilter.java @@ -17,46 +17,34 @@ package org.apache.servicecomb.core.registry.discovery; -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; -import org.apache.servicecomb.core.bootstrap.SCBBootstrap; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; +import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.registry.discovery.DiscoveryContext; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Mocked; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; +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 org.mockito.Mockito; public class TestEndpointDiscoveryFilter { EndpointDiscoveryFilter filter = new EndpointDiscoveryFilter(); DiscoveryContext context = new DiscoveryContext(); - @Mocked Invocation invocation; - SCBEngine scbEngine; - - @Before + @BeforeEach public void setup() { - ArchaiusUtils.resetConfig(); - ConfigUtil.installDynamicConfig(); - context.setInputParameters(invocation); - scbEngine = SCBBootstrap.createSCBEngineForTest(); + } - @After + @AfterEach public void teardown() { - scbEngine.destroy(); - ArchaiusUtils.resetConfig(); } @Test @@ -66,36 +54,38 @@ public void getOrder() { @Test public void getTransportName() { - new Expectations() { - { - invocation.getConfigTransportName(); - result = Const.RESTFUL; - } - }; - - Assertions.assertEquals(Const.RESTFUL, filter.findTransportName(context, null)); + invocation = Mockito.mock(Invocation.class); + Mockito.when(invocation.getConfigTransportName()).thenReturn(CoreConst.RESTFUL); + context.setInputParameters(invocation); + Assertions.assertEquals(CoreConst.RESTFUL, filter.findTransportName(context, null)); } @Test public void createEndpointNullTransport() { - Assertions.assertNull(filter.createEndpoint(null, Const.RESTFUL, "", null)); + TransportManager transportManager = Mockito.mock(TransportManager.class); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); + Mockito.when(transportManager.findTransport(CoreConst.RESTFUL)).thenReturn(null); + filter.setScbEngine(scbEngine); + Assertions.assertNull(filter.createEndpoint(null, CoreConst.RESTFUL, "", null)); } @Test - public void createEndpointNormal(@Mocked Transport transport, @Mocked MicroserviceInstance instance) { + public void createEndpointNormal() { + Transport transport = Mockito.mock(Transport.class); + StatefulDiscoveryInstance instance = Mockito.mock(StatefulDiscoveryInstance.class); + TransportManager transportManager = Mockito.mock(TransportManager.class); + String endpoint = "rest://ip:port"; Object address = new Object(); - new Expectations(scbEngine.getTransportManager()) { - { - scbEngine.getTransportManager().findTransport(Const.RESTFUL); - result = transport; - transport.parseAddress(endpoint); - result = address; - } - }; + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); + Mockito.when(transportManager.findTransport(CoreConst.RESTFUL)).thenReturn(transport); + Mockito.when(transport.parseAddress(endpoint)).thenReturn(address); + filter.setScbEngine(scbEngine); - Endpoint ep = (Endpoint) filter.createEndpoint(null, Const.RESTFUL, endpoint, instance); + Endpoint ep = (Endpoint) filter.createEndpoint(null, CoreConst.RESTFUL, endpoint, instance); Assertions.assertSame(transport, ep.getTransport()); Assertions.assertSame(address, ep.getAddress()); Assertions.assertSame(instance, ep.getMicroserviceInstance()); diff --git a/core/src/test/java/org/apache/servicecomb/core/tracing/BraveTraceIdGeneratorTest.java b/core/src/test/java/org/apache/servicecomb/core/tracing/BraveTraceIdGeneratorTest.java index 55fa4538750..e858dd954c5 100644 --- a/core/src/test/java/org/apache/servicecomb/core/tracing/BraveTraceIdGeneratorTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/tracing/BraveTraceIdGeneratorTest.java @@ -17,8 +17,8 @@ package org.apache.servicecomb.core.tracing; -import org.junit.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class BraveTraceIdGeneratorTest { diff --git a/core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java b/core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java index b0fd7ba0c4b..65c44b12560 100644 --- a/core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java +++ b/core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java @@ -17,35 +17,25 @@ package org.apache.servicecomb.core.transport; +import static org.apache.servicecomb.core.transport.AbstractTransport.PUBLISH_ADDRESS; + import java.io.UnsupportedEncodingException; -import java.lang.reflect.Method; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Collections; -import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.net.IpPort; -import org.apache.servicecomb.foundation.vertx.VertxUtils; -import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.swagger.invocation.AsyncResponse; -import org.junit.AfterClass; -import org.junit.Test; import org.junit.jupiter.api.Assertions; -import org.springframework.util.ReflectionUtils; - -import com.netflix.config.DynamicProperty; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; -import mockit.Expectations; -import mockit.Mocked; +import io.vertx.core.impl.SysProps; public class TestAbstractTransport { - private final Method updatePropertyMethod = - ReflectionUtils.findMethod(DynamicProperty.class, "updateProperty", String.class, Object.class); - - private void updateProperty(String key, Object value) { - updatePropertyMethod.setAccessible(true); - ReflectionUtils.invokeMethod(updatePropertyMethod, null, key, value); - } + Environment environment = Mockito.mock(Environment.class); static class MyAbstractTransport extends AbstractTransport { @@ -58,26 +48,23 @@ public String getName() { public boolean init() { return true; } - - @Override - public void send(Invocation invocation, AsyncResponse asyncResp) { - } } - @AfterClass - public static void classTeardown() { - VertxUtils.blockCloseVertxByName("transport"); + @BeforeEach + public void setUp() { + Mockito.when(environment.getProperty(PUBLISH_ADDRESS, "")).thenReturn(""); + Mockito.when(environment.getProperty("servicecomb.my.publishPort", int.class, 0)).thenReturn(0); + Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) + .thenReturn(true); + LegacyPropertyFactory.setEnvironment(environment); } @Test public void testSetListenAddressWithoutSchemaChineseSpaceNewSC() throws UnsupportedEncodingException { - new Expectations() { - { - RegistrationManager.getPublishAddress("my", "127.0.0.1:9090"); - } - }; - MyAbstractTransport transport = new MyAbstractTransport(); + transport.setEnvironment(environment); transport.setListenAddressWithoutSchema("127.0.0.1:9090", Collections.singletonMap("country", "中 国")); Assertions.assertEquals("my://127.0.0.1:9090?country=" + URLEncoder.encode("中 国", StandardCharsets.UTF_8.name()), transport.getEndpoint().getEndpoint()); @@ -86,6 +73,7 @@ public void testSetListenAddressWithoutSchemaChineseSpaceNewSC() throws Unsuppor @Test public void testSetListenAddressWithoutSchemaNormalNotEncode() { MyAbstractTransport transport = new MyAbstractTransport(); + transport.setEnvironment(environment); transport.setListenAddressWithoutSchema("127.0.0.1:9090", Collections.singletonMap("country", "chinese")); Assertions.assertEquals("my://127.0.0.1:9090?country=chinese", transport.getEndpoint().getEndpoint()); } @@ -93,6 +81,7 @@ public void testSetListenAddressWithoutSchemaNormalNotEncode() { @Test public void testSetListenAddressWithoutSchemaAlreadyHaveQuery() { MyAbstractTransport transport = new MyAbstractTransport(); + transport.setEnvironment(environment); transport.setListenAddressWithoutSchema("127.0.0.1:9090?a=aValue", Collections.singletonMap("country", "chinese")); Assertions.assertEquals("my://127.0.0.1:9090?a=aValue&country=chinese", transport.getEndpoint().getEndpoint()); @@ -101,6 +90,7 @@ public void testSetListenAddressWithoutSchemaAlreadyHaveQuery() { @Test public void testMyAbstractTransport() { MyAbstractTransport transport = new MyAbstractTransport(); + transport.setEnvironment(environment); transport.setListenAddressWithoutSchema("127.0.0.1:9090"); Assertions.assertEquals("my", transport.getName()); Assertions.assertEquals("my://127.0.0.1:9090", transport.getEndpoint().getEndpoint()); @@ -112,10 +102,11 @@ public void testMyAbstractTransport() { Assertions.assertNull(transport.parseAddress(null)); } - @Test(expected = IllegalArgumentException.class) - public void testMyAbstractTransportException(@Mocked TransportManager manager) { + @Test + public void testMyAbstractTransportException() { MyAbstractTransport transport = new MyAbstractTransport(); - - transport.setListenAddressWithoutSchema(":127.0.0.1:9090"); + transport.setEnvironment(environment); + Assertions.assertThrows(IllegalArgumentException.class, () -> + transport.setListenAddressWithoutSchema(":127.0.0.1:9090")); } } diff --git a/core/src/test/java/org/apache/servicecomb/core/transport/TestTransportManager.java b/core/src/test/java/org/apache/servicecomb/core/transport/TestTransportManager.java index 208224515c9..89b591beecb 100644 --- a/core/src/test/java/org/apache/servicecomb/core/transport/TestTransportManager.java +++ b/core/src/test/java/org/apache/servicecomb/core/transport/TestTransportManager.java @@ -25,30 +25,20 @@ import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Injectable; -import mockit.Mocked; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; public class TestTransportManager { @Test - public void testTransportManagerInitFail(@Mocked SCBEngine scbEngine, @Injectable Transport transport) + public void testTransportManagerInitFail() throws Exception { - new Expectations() { - { - transport.getName(); - result = "test"; - transport.init(); - result = false; - transport.canInit(); - result = true; - } - }; + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Transport transport = Mockito.mock(Transport.class); + Mockito.when(transport.getName()).thenReturn("test"); + Mockito.when(transport.init()).thenReturn(false); + Mockito.when(transport.canInit()).thenReturn(true); List transports = Arrays.asList(transport); - TransportManager manager = new TransportManager(); manager.addTransportsBeforeInit(transports); @@ -57,20 +47,14 @@ public void testTransportManagerInitFail(@Mocked SCBEngine scbEngine, @Injectabl } @Test - public void testTransportManagerInitSucc(@Mocked SCBEngine scbEngine, @Injectable Transport transport, - @Injectable Endpoint endpoint, @Injectable MicroserviceInstance instance) throws Exception { - new Expectations() { - { - transport.getName(); - result = "test"; - transport.canInit(); - result = true; - transport.init(); - result = true; - transport.getPublishEndpoint(); - result = endpoint; - } - }; + public void testTransportManagerInitSuccess() throws Exception { + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Transport transport = Mockito.mock(Transport.class); + Endpoint endpoint = Mockito.mock(Endpoint.class); + Mockito.when(transport.getName()).thenReturn("test"); + Mockito.when(transport.init()).thenReturn(true); + Mockito.when(transport.canInit()).thenReturn(true); + Mockito.when(transport.getPublishEndpoint()).thenReturn(endpoint); List transports = Arrays.asList(transport); TransportManager manager = new TransportManager(); @@ -81,42 +65,31 @@ public void testTransportManagerInitSucc(@Mocked SCBEngine scbEngine, @Injectabl } @Test - public void testGroupByName(@Mocked Transport t1, @Mocked Transport t2_1, @Mocked Transport t2_2) { - new Expectations() { - { - t1.getName(); - result = "t1"; - - t2_1.getName(); - result = "t2"; - t2_2.getName(); - result = "t2"; - } - }; - + public void testGroupByName() { + Transport t1 = Mockito.mock(Transport.class); + Mockito.when(t1.getName()).thenReturn("t1"); + Transport t21 = Mockito.mock(Transport.class); + Mockito.when(t21.getName()).thenReturn("t2"); + Transport t22 = Mockito.mock(Transport.class); + Mockito.when(t22.getName()).thenReturn("t2"); TransportManager manager = new TransportManager(); - manager.addTransportsBeforeInit(Arrays.asList(t1, t2_1, t2_2)); + manager.addTransportsBeforeInit(Arrays.asList(t1, t21, t22)); Map> groups = manager.groupByName(); Assertions.assertEquals(2, groups.size()); Assertions.assertEquals(1, groups.get("t1").size()); Assertions.assertEquals(t1, groups.get("t1").get(0)); Assertions.assertEquals(2, groups.get("t2").size()); - Assertions.assertEquals(t2_1, groups.get("t2").get(0)); - Assertions.assertEquals(t2_2, groups.get("t2").get(1)); + Assertions.assertEquals(t21, groups.get("t2").get(0)); + Assertions.assertEquals(t22, groups.get("t2").get(1)); } @Test - public void testCheckTransportGroupInvalid(@Mocked Transport t1, @Mocked Transport t2) { - new Expectations() { - { - t1.getOrder(); - result = 1; - - t2.getOrder(); - result = 1; - } - }; + public void testCheckTransportGroupInvalid() { + Transport t1 = Mockito.mock(Transport.class); + Mockito.when(t1.getOrder()).thenReturn(1); + Transport t2 = Mockito.mock(Transport.class); + Mockito.when(t2.getOrder()).thenReturn(1); TransportManager manager = new TransportManager(); List group = Arrays.asList(t1, t2); @@ -125,23 +98,16 @@ public void testCheckTransportGroupInvalid(@Mocked Transport t1, @Mocked Transpo manager.checkTransportGroup(group); Assertions.fail("must throw exception"); } catch (ServiceCombException e) { - Assertions.assertEquals( - "org.apache.servicecomb.core.$Impl_Transport and org.apache.servicecomb.core.$Impl_Transport have the same order 1", - e.getMessage()); + Assertions.assertTrue(e.getMessage().contains("have the same order")); } } @Test - public void testCheckTransportGroupValid(@Mocked Transport t1, @Mocked Transport t2) { - new Expectations() { - { - t1.getOrder(); - result = 1; - - t2.getOrder(); - result = 2; - } - }; + public void testCheckTransportGroupValid() { + Transport t1 = Mockito.mock(Transport.class); + Mockito.when(t1.getOrder()).thenReturn(1); + Transport t2 = Mockito.mock(Transport.class); + Mockito.when(t2.getOrder()).thenReturn(2); TransportManager manager = new TransportManager(); List group = Arrays.asList(t1, t2); @@ -154,18 +120,12 @@ public void testCheckTransportGroupValid(@Mocked Transport t1, @Mocked Transport } @Test - public void testChooseOneTransportFirst(@Mocked Transport t1, @Mocked Transport t2) { - new Expectations() { - { - t1.getOrder(); - result = 1; - t1.canInit(); - result = true; - - t2.getOrder(); - result = 2; - } - }; + public void testChooseOneTransportFirst() { + Transport t1 = Mockito.mock(Transport.class); + Mockito.when(t1.getOrder()).thenReturn(1); + Mockito.when(t1.canInit()).thenReturn(true); + Transport t2 = Mockito.mock(Transport.class); + Mockito.when(t2.getOrder()).thenReturn(2); TransportManager manager = new TransportManager(); List group = Arrays.asList(t1, t2); @@ -174,21 +134,13 @@ public void testChooseOneTransportFirst(@Mocked Transport t1, @Mocked Transport } @Test - public void testChooseOneTransportSecond(@Mocked Transport t1, @Mocked Transport t2) { - new Expectations() { - { - t1.getOrder(); - result = Integer.MAX_VALUE; - t1.canInit(); - result = true; - - t2.getOrder(); - result = -1000; - t2.canInit(); - result = false; - } - }; - + public void testChooseOneTransportSecond() { + Transport t1 = Mockito.mock(Transport.class); + Mockito.when(t1.getOrder()).thenReturn(Integer.MAX_VALUE); + Mockito.when(t1.canInit()).thenReturn(true); + Transport t2 = Mockito.mock(Transport.class); + Mockito.when(t2.getOrder()).thenReturn(-1000); + Mockito.when(t2.canInit()).thenReturn(false); TransportManager manager = new TransportManager(); List group = Arrays.asList(t1, t2); @@ -196,22 +148,14 @@ public void testChooseOneTransportSecond(@Mocked Transport t1, @Mocked Transport } @Test - public void testChooseOneTransportNone(@Mocked Transport t1, @Mocked Transport t2) { - new Expectations() { - { - t1.getName(); - result = "t"; - t1.getOrder(); - result = 1; - t1.canInit(); - result = false; - - t2.getOrder(); - result = 2; - t2.canInit(); - result = false; - } - }; + public void testChooseOneTransportNone() { + Transport t1 = Mockito.mock(Transport.class); + Mockito.when(t1.getName()).thenReturn("t"); + Mockito.when(t1.getOrder()).thenReturn(1); + Mockito.when(t1.canInit()).thenReturn(false); + Transport t2 = Mockito.mock(Transport.class); + Mockito.when(t2.getOrder()).thenReturn(2); + Mockito.when(t2.canInit()).thenReturn(false); TransportManager manager = new TransportManager(); List group = Arrays.asList(t1, t2); diff --git a/core/src/test/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter b/core/src/test/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter deleted file mode 100644 index ff338ab9d23..00000000000 --- a/core/src/test/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.core.handler.impl.ErrorToProducerResponseConverter \ No newline at end of file diff --git a/core/src/test/resources/microservice.yaml b/core/src/test/resources/microservice.yaml index 0baa251fd13..b17313cd44c 100644 --- a/core/src/test/resources/microservice.yaml +++ b/core/src/test/resources/microservice.yaml @@ -30,4 +30,4 @@ servicecomb: zq: - tlist: a - tlist: b - - tlist: 1 \ No newline at end of file + - tlist: 1 diff --git a/core/src/test/resources/test/test/microservice.yaml b/core/src/test/resources/test/test/microservice.yaml index a82dcd3f036..7ec47b07b63 100644 --- a/core/src/test/resources/test/test/microservice.yaml +++ b/core/src/test/resources/test/test/microservice.yaml @@ -18,7 +18,7 @@ service_description: name: default version: 0.0.1 - propertyExtentedClass: org.apache.servicecomb.serviceregistry.MicroServicePropertyExtendedStub + propertyExtendedClass: org.apache.servicecomb.serviceregistry.MicroServicePropertyExtendedStub servicecomb: service: registry: diff --git a/coverage-reports/pom.xml b/coverage-reports/pom.xml index e5204622277..97f795fcbf5 100644 --- a/coverage-reports/pom.xml +++ b/coverage-reports/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default 4.0.0 @@ -77,12 +77,6 @@ java-chassis-core - - - org.apache.servicecomb - deployment - - org.apache.servicecomb @@ -177,12 +171,6 @@ handler-tracing-zipkin - - - org.apache.servicecomb - inspector - - org.apache.servicecomb @@ -220,10 +208,6 @@ org.apache.servicecomb registry-local - - org.apache.servicecomb - registry-schema-discovery - org.apache.servicecomb registry-service-center @@ -240,10 +224,6 @@ - - org.apache.servicecomb - java-chassis-spring-boot-starter - org.apache.servicecomb java-chassis-spring-boot-starter-servlet @@ -254,10 +234,6 @@ - - org.apache.servicecomb - servicestage-environment - org.apache.servicecomb servicestage @@ -510,19 +486,19 @@ org.apache.servicecomb.demo - demo-zeroconfig-schemadiscovery-registry-client + demo-zeroconfig-registry-client ${project.version} test org.apache.servicecomb.demo - demo-zeroconfig-schemadiscovery-registry-server + demo-zeroconfig-registry-server ${project.version} test org.apache.servicecomb.demo - demo-zeroconfig-schemadiscovery-registry-tests + demo-zeroconfig-registry-tests ${project.version} test diff --git a/demo/README.md b/demo/README.md index c646b9abd9f..18153ce7a57 100644 --- a/demo/README.md +++ b/demo/README.md @@ -1,9 +1,9 @@ -This module contains integrated test cases. Running these test cases, should include profile +This module contains integrated test cases. Running these test cases, should include profile ```shell script -Pdocker ``` [java-chassis-samples][java-chassis-samples] project provides a lot of samples. - + [java-chassis-samples]: https://github.com/apache/servicecomb-samples/tree/master/java-chassis-samples diff --git a/demo/demo-consul/README.md b/demo/demo-consul/README.md new file mode 100644 index 00000000000..872a1d2e93a --- /dev/null +++ b/demo/demo-consul/README.md @@ -0,0 +1,5 @@ +# Notice + +This integration tests is designed for Consul registry and configuration. And extra test cases include: + +* Test cases related to SpringMVC annotations that demo-springmvc can not cover. diff --git a/demo/demo-consul/consumer/pom.xml b/demo/demo-consul/consumer/pom.xml new file mode 100644 index 00000000000..eccc75e41b1 --- /dev/null +++ b/demo/demo-consul/consumer/pom.xml @@ -0,0 +1,104 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-consul + 3.4.0-SNAPSHOT + + + consul-consumer + Java Chassis::Demo::Consul::CONSUMER + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + registry-consul + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java new file mode 100644 index 00000000000..f71738a9ed6 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.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.servicecomb.samples; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.ServerWebSocket; +import io.vertx.core.http.WebSocket; + +@RestSchema(schemaId = "ClientWebsocketController") +@RequestMapping(path = "/ws") +public class ClientWebsocketController { + interface ProviderService { + WebSocket websocket(); + } + + @RpcReference(schemaId = "WebsocketController", microserviceName = "provider") + private ProviderService providerService; + + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + public void websocket(ServerWebSocket serverWebsocket) { + WebSocket providerWebSocket = providerService.websocket(); + providerWebSocket.closeHandler(v -> serverWebsocket.close()); + providerWebSocket.textMessageHandler(m -> { + serverWebsocket.writeTextMessage(m); + }); + serverWebsocket.textMessageHandler(m -> { + providerWebSocket.writeTextMessage(m); + }); + } +} diff --git a/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsulConsumerApplication.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsulConsumerApplication.java new file mode 100644 index 00000000000..da243836198 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsulConsumerApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ConsulConsumerApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsulConsumerApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java new file mode 100644 index 00000000000..e66fb2cb3d0 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.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.servicecomb.samples; + +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@RestSchema(schemaId = "ConsumerController") +@RequestMapping(path = "/") +public class ConsumerController { + @RpcReference(schemaId = "ProviderController", microserviceName = "provider") + private ProviderService providerService; + + // consumer service which delegate the implementation to provider service. + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { + return providerService.sayHello(name); + } + + @GetMapping("/getConfig") + public String getConfig(@RequestParam("key") String key) { + return providerService.getConfig(key); + } + + @PostMapping(path = "/testContentType", consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public User testContentType(@RequestBody User user) { + return providerService.testContentType(user); + } +} diff --git a/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java new file mode 100644 index 00000000000..f0e894d6745 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) +public class ConsumerHeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { + @RpcReference(microserviceName = "provider", schemaId = "HeaderParamWithListSchema") + private IHeaderParamWithListSchemaSpringMvc provider; + + @Override + public String headerListDefault(List headerList) { + return provider.headerListDefault(headerList); + } + + @Override + public String headerListCSV(List headerList) { + return provider.headerListCSV(headerList); + } + + @Override + public String headerListMULTI(List headerList) { + return provider.headerListMULTI(headerList); + } + + @Override + public String headerListSSV(List headerList) { + return provider.headerListSSV(headerList); + } + + @Override + public String headerListPIPES(List headerList) { + return provider.headerListPIPES(headerList); + } +} diff --git a/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java new file mode 100644 index 00000000000..aa169801cf7 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +public class ConsumerReactiveStreamController { + interface ProviderReactiveStreamController { + Publisher sseString(); + + Publisher sseModel(); + } + + @RpcReference(microserviceName = "provider", schemaId = "ReactiveStreamController") + ProviderReactiveStreamController controller; + + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + @Transport(name = CoreConst.RESTFUL) + public Publisher sseString() { + return controller.sseString(); + } + + @GetMapping("/sseModel") + @Transport(name = CoreConst.RESTFUL) + public Publisher sseModel() { + return controller.sseModel(); + } +} diff --git a/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java new file mode 100644 index 00000000000..2899546c752 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.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.servicecomb.samples; + +public interface ProviderService { + String sayHello(String name); + + String getConfig(String key); + + User testContentType(User user); +} diff --git a/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/User.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/User.java new file mode 100644 index 00000000000..ff421930ef2 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/User.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.servicecomb.samples; + + +public class User { + + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/demo/demo-consul/consumer/src/main/resources/application.yml b/demo/demo-consul/consumer/src/main/resources/application.yml new file mode 100644 index 00000000000..ef0d45c1dcb --- /dev/null +++ b/demo/demo-consul/consumer/src/main/resources/application.yml @@ -0,0 +1,40 @@ +# +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: demo-consul + version: 0.0.1 + name: consumer + properties: + group: green + + registry: + consul: + enabled: true + host: 127.0.0.1 + scheme: http + discovery: + enabled: true + acl-token: '' + watch-seconds: 8 + + rest: + address: 0.0.0.0:9092?websocketEnabled=true + server: + websocket-prefix: /ws diff --git a/demo/demo-consul/consumer/src/main/resources/log4j2.xml b/demo/demo-consul/consumer/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-consul/gateway/pom.xml b/demo/demo-consul/gateway/pom.xml new file mode 100644 index 00000000000..1dad3860e7f --- /dev/null +++ b/demo/demo-consul/gateway/pom.xml @@ -0,0 +1,107 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-consul + 3.4.0-SNAPSHOT + + + consul-gateway + Java Chassis::Demo::Consul::GATEWAY + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + edge-core + + + org.apache.servicecomb + registry-consul + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-consul/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java b/demo/demo-consul/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java new file mode 100644 index 00000000000..7d58caafd9d --- /dev/null +++ b/demo/demo-consul/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class GatewayApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-consul/gateway/src/main/resources/application.yml b/demo/demo-consul/gateway/src/main/resources/application.yml new file mode 100644 index 00000000000..9855a87a1de --- /dev/null +++ b/demo/demo-consul/gateway/src/main/resources/application.yml @@ -0,0 +1,60 @@ +# +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: demo-consul + version: 0.0.1 + name: gateway + + registry: + consul: + enabled: true + host: 127.0.0.1 + scheme: http + discovery: + enabled: true + acl-token: '' + watch-seconds: 8 + + rest: + address: 0.0.0.0:9090?websocketEnabled=true + server: + websocket-prefix: /ws + + http: + dispatcher: + edge: + default: + enabled: false + url: + enabled: true + pattern: /(.*) + mappings: + consumer: + prefixSegmentCount: 0 + path: "/.*" + microserviceName: consumer + versionRule: 0.0.0+ + websocket: + mappings: + consumer: + prefixSegmentCount: 0 + path: "/ws/.*" + microserviceName: consumer + versionRule: 0.0.0+ diff --git a/demo/demo-consul/gateway/src/main/resources/log4j2.xml b/demo/demo-consul/gateway/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-consul/gateway/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-consul/pom.xml b/demo/demo-consul/pom.xml new file mode 100644 index 00000000000..aaa8f3b91ce --- /dev/null +++ b/demo/demo-consul/pom.xml @@ -0,0 +1,62 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + + demo-consul + Java Chassis::Demo::Consul + pom + + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + solution-basic + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-api + + + + + provider + consumer + gateway + test-client + + diff --git a/demo/demo-consul/provider/pom.xml b/demo/demo-consul/provider/pom.xml new file mode 100644 index 00000000000..0986f9f4342 --- /dev/null +++ b/demo/demo-consul/provider/pom.xml @@ -0,0 +1,114 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-consul + 3.4.0-SNAPSHOT + + + consul-provider + Java Chassis::Demo::Consul::PROVIDER + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + registry-consul + + + org.apache.servicecomb + config-consul + + + + io.reactivex.rxjava3 + rxjava + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ConsulProviderApplication.java b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ConsulProviderApplication.java new file mode 100644 index 00000000000..b84da167943 --- /dev/null +++ b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ConsulProviderApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ConsulProviderApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsulProviderApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java new file mode 100644 index 00000000000..8a773f91c72 --- /dev/null +++ b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) +public class HeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { + @Override + public String headerListDefault(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListCSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListMULTI(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListSSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListPIPES(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } +} diff --git a/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java new file mode 100644 index 00000000000..677c20ed106 --- /dev/null +++ b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@RestSchema(schemaId = "ProviderController") +@RequestMapping(path = "/") +public class ProviderController implements InitializingBean { + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + // a very simple service to echo the request parameter + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { +// return "Hello " + environment.getProperty("servicecomb.rest.address"); + return "Hello " + name; + } + + @GetMapping("/getConfig") + public String getConfig(@RequestParam("key") String key) { + return environment.getProperty(key); + } + + @PostMapping(path = "/testContentType", consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public User testContentType(@RequestBody User user) { + return user; + } + + @Override + public void afterPropertiesSet() throws Exception { + } +} diff --git a/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java new file mode 100644 index 00000000000..8108d15fd46 --- /dev/null +++ b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.reactivex.rxjava3.core.Flowable; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +@Transport(name = CoreConst.RESTFUL) +public class ReactiveStreamController { + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + public Publisher sseString() { + return Flowable.fromArray("a", "b", "c"); + } + + @GetMapping("/sseModel") + public Publisher sseModel() { + return Flowable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS) + .map(item -> new Model("jack", item.intValue())); + } +} diff --git a/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/User.java b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/User.java new file mode 100644 index 00000000000..ca12402d672 --- /dev/null +++ b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/User.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + +public class User { + + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java new file mode 100644 index 00000000000..6381ef1d54c --- /dev/null +++ b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.ServerWebSocket; + +@RestSchema(schemaId = "WebsocketController") +@RequestMapping(path = "/ws") +public class WebsocketController { + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + public void websocket(ServerWebSocket serverWebsocket) { + AtomicInteger receiveCount = new AtomicInteger(0); + CountDownLatch startSend = new CountDownLatch(1); + serverWebsocket.textMessageHandler(s -> { + if ("start".equals(s)) { + startSend.countDown(); + serverWebsocket.writeTextMessage("started"); + return; + } + receiveCount.getAndIncrement(); + }); + serverWebsocket.closeHandler((v) -> System.out.println("closed")); + + new Thread(() -> { + try { + startSend.await(30, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + serverWebsocket.writeTextMessage("hello"); + + for (int i = 0; i < 5; i++) { + serverWebsocket.writeTextMessage("hello " + i); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + serverWebsocket.writeTextMessage("total " + receiveCount.get()); + serverWebsocket.close(); + }).start(); + } +} diff --git a/demo/demo-consul/provider/src/main/resources/application.yml b/demo/demo-consul/provider/src/main/resources/application.yml new file mode 100644 index 00000000000..31bbfa5d4c7 --- /dev/null +++ b/demo/demo-consul/provider/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- + +# spring boot configurations +servicecomb: + service: + application: demo-consul + version: 0.0.1 + name: provider + properties: + group: green + + registry: + consul: + enabled: true + host: 127.0.0.1 + scheme: http + discovery: + enabled: true + acl-token: '' + watch-seconds: 8 + + config: + consul: + enabled: true + host: 127.0.0.1 + scheme: http + acl-token: '' + watch-seconds: 8 + + rest: + address: 0.0.0.0:9094?websocketEnabled=true + server: + websocket-prefix: /ws + + cors: + enabled: true + origin: "*" + allowCredentials: false + allowedMethod: "*" + maxAge: 3600 + + +key1: 1 +key2: 3 +key3: 5 + +test1: env +test2: applition +test3: service +test4: version +test5: tag diff --git a/demo/demo-consul/provider/src/main/resources/log4j2.xml b/demo/demo-consul/provider/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-consul/provider/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-consul/test-client/pom.xml b/demo/demo-consul/test-client/pom.xml new file mode 100644 index 00000000000..791ac474886 --- /dev/null +++ b/demo/demo-consul/test-client/pom.xml @@ -0,0 +1,220 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-consul + 3.4.0-SNAPSHOT + + + consul-test-client + Java Chassis::Demo::Consul::TEST-CLIENT + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + registry-local + + + org.kiwiproject + consul-client + + + com.google.code.gson + gson + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + hashicorp/consul:1.21 + consul + + alias + + consulServer + + + 8500 + + + + + + 8500:8500 + + + yes + + + + + consul-provider:${project.version} + consul-provider + + alias + + + -Dservicecomb.registry.consul.enabled=true + -Dservicecomb.registry.consul.scheme=http + -Dservicecomb.registry.consul.host=consul + -Dservicecomb.registry.consul.port=8500 + -Dservicecomb.config.consul.enabled=true + -Dservicecomb.config.consul.scheme=http + -Dservicecomb.config.consul.host=consul + -Dservicecomb.config.consul.port=8500 + + /maven/maven/consul-provider-${project.version}.jar + + + consul:consul + + + ServiceComb is ready + + + 9094 + + + + + + 9094:9094 + + + + + consul-consumer:${project.version} + consul-consumer + + alias + + + -Dservicecomb.registry.consul.enabled=true + -Dservicecomb.registry.consul.scheme=http + -Dservicecomb.registry.consul.host=consul + -Dservicecomb.registry.consul.port=8500 + + /maven/maven/consul-consumer-${project.version}.jar + + + consul:consul + + + ServiceComb is ready + + + 9092 + + + + + + 9092:9092 + + + + + consul-gateway:${project.version} + consul-gateway + + alias + + + -Dservicecomb.registry.consul.enabled=true + -Dservicecomb.registry.consul.scheme=http + -Dservicecomb.registry.consul.host=consul + -Dservicecomb.registry.consul.port=8500 + + /maven/maven/consul-gateway-${project.version}.jar + + + consul:consul + + + ServiceComb is ready + + + 9090 + + + + + + 9090:9090 + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/Config.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/Config.java new file mode 100644 index 00000000000..2e9105cdd4a --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/Config.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +public interface Config { + String GATEWAY_URL = "http://localhost:9090"; +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ConsulConfigIT.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ConsulConfigIT.java new file mode 100644 index 00000000000..1a7be4b874d --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ConsulConfigIT.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.servicecomb.samples; + +import com.google.common.net.HostAndPort; +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.utils.ConditionWaiter.SleepUtil; +import org.kiwiproject.consul.Consul; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +import java.util.concurrent.TimeUnit; + +@Component +public class ConsulConfigIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + private static final Logger LOGGER = LoggerFactory.getLogger(ConsulConfigIT.class); + + @Override + public void testRestTransport() throws Exception { + + testEnvironment(); + testApplication(); + testService(); + testVersion(); + testTag(); + testOverride(); + } + + private void testOverride() { + + putValue("/servicecomb/config/environment/production/application2.properties", + "testValue=t1"); + putValue("/servicecomb/config/application/production/demo-consul/application2.properties", + "testValue=t2"); + testGetConfig("testValue", "t2"); + putValue("/servicecomb/config/service/production/demo-consul/provider/application2.properties", + "testValue=t3"); + testGetConfig("testValue", "t3"); + putValue("/servicecomb/config/version/production/demo-consul/provider/0.0.1/application2.properties", + "testValue=t4"); + testGetConfig("testValue", "t4"); + putValue("/servicecomb/config/tag/production/demo-consul/provider/0.0.1/tag1/application2.properties", + "testValue=t5"); + testGetConfig("testValue", "t5"); + } + + private void testEnvironment() { + + putValue("/servicecomb/config/environment/production/application.properties", + "test1=env"); + putValue("/servicecomb/config/environment/production/application.properties", + "test1=env1"); + + testGetConfig("test1", "env1"); + } + + + private void testApplication() { + + putValue("/servicecomb/config/application/production/demo-consul/application.properties", + "test2=applition"); + putValue("/servicecomb/config/application/production/demo-consul/application.properties", + "test2=applition2"); + testGetConfig("test2", "applition2"); + } + + private void testService() { + + putValue("/servicecomb/config/service/production/demo-consul/provider/application.properties", + "test3=service"); + putValue("/servicecomb/config/service/production/demo-consul/provider/application.properties", + "test3=service3"); + testGetConfig("test3", "service3"); + } + + private void testVersion() { + + putValue("/servicecomb/config/version/production/demo-consul/provider/0.0.1/application.properties", + "test3=version"); + putValue("/servicecomb/config/version/production/demo-consul/provider/0.0.1/application.properties", + "test4=version4"); + testGetConfig("test4", "version4"); + } + + private void testTag() { + + putValue("/servicecomb/config/tag/production/demo-consul/provider/0.0.1/tag1/application.properties", + "test5=tag"); + putValue("/servicecomb/config/tag/production/demo-consul/provider/0.0.1/tag1/application.properties", + "test5=tag5"); + testGetConfig("test5", "tag5"); + } + + public void putValue(String key, String value) { + try { + Consul.Builder builder = Consul.builder().withHostAndPort(HostAndPort.fromParts("127.0.0.1", 8500)); + Consul consulClient = builder.build(); + consulClient.keyValueClient().putValue(key, value); + LOGGER.info("Value set successfully:{}", value); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void testGetConfig(String key, String expectValue) { + + String result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=" + key, String.class); + + for (int i = 0; i < 4; i++) { + if (StringUtils.equals(expectValue, result)) { + TestMgr.check(expectValue, result); + break; + } + SleepUtil.sleep(500, TimeUnit.MILLISECONDS); + } + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java new file mode 100644 index 00000000000..1b129acc2c6 --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HeaderParamWithListSchemaIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHeaderListDefault(); + testHeaderListMulti(); + testHeaderListCSV(); + testHeaderListSSV(); + testHeaderListPipes(); + } + + // default to multi + private void testHeaderListDefault() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListDefault", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListPipes() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a|b|c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListPIPES", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListSSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a b c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListSSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListCSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a,b,c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + + headers.add("headerList", "a, b, c"); + entity = new HttpEntity<>(headers); + result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListMulti() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListMULTI", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java new file mode 100644 index 00000000000..97e883fb45f --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.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.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HelloWorldIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHelloWorld(); + testGetConfig(); + } + + private void testGetConfig() { + String result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key1", String.class); + TestMgr.check("1", result); + result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key2", String.class); + TestMgr.check("3", result); + result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key3", String.class); + TestMgr.check("5", result); + } + + private void testHelloWorld() { + String result = template + .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); + TestMgr.check("Hello World", result); + + // test trace id added + MultiValueMap headers = new HttpHeaders(); + headers.add("X-B3-TraceId", "81de2eb7691c2bbb"); + HttpEntity entity = new HttpEntity(headers); + ResponseEntity response = + template.exchange(Config.GATEWAY_URL + "/sayHello?name=World", HttpMethod.GET, entity, String.class); + TestMgr.check(1, response.getHeaders().get("X-B3-TraceId").size()); + TestMgr.check("81de2eb7691c2bbb", response.getHeaders().getFirst("X-B3-TraceId")); + TestMgr.check("Hello World", response.getBody()); + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java new file mode 100644 index 00000000000..2bdd0ca42ca --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class ProviderIT implements CategorizedTestCase { + + RestOperations template = new RestTemplate(); + + private static final Logger LOGGER = LoggerFactory.getLogger(ProviderIT.class); + + @Override + public void testRestTransport() throws Exception { + + User user = getUser("Application/json"); + TestMgr.check(1L, user.getId()); + TestMgr.check("czd", user.getName()); + + User user2 = getUser("application/json"); + TestMgr.check(1L, user2.getId()); + TestMgr.check("czd", user2.getName()); + + User user3 = getUser("APPLICATION/JSON"); + TestMgr.check(1L, user3.getId()); + TestMgr.check("czd", user3.getName()); + } + + private User getUser(String contentType) throws IOException { + HttpHeaders headers = new HttpHeaders(); + headers.set("Content-Type", contentType); + + String requestBody = """ + { + "id": 1, + "name": "czd" + } + """; + + HttpEntity entity = new HttpEntity<>(requestBody, headers); + + String url = Config.GATEWAY_URL + "/testContentType"; + ResponseEntity response = template.exchange( + url, HttpMethod.POST, entity, String.class); + + User user = JsonUtils.readValue(response.getBody().getBytes(StandardCharsets.UTF_8), User.class); + return user; + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java new file mode 100644 index 00000000000..488a6cf712a --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient.Model; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component +public class ReactiveStreamIT implements CategorizedTestCase { + @Autowired + @Qualifier("reactiveStreamProvider") + ReactiveStreamClient reactiveStreamProvider; + + @Autowired + @Qualifier("reactiveStreamGateway") + ReactiveStreamClient reactiveStreamGateway; + + @Override + public void testRestTransport() throws Exception { + testSseString(reactiveStreamProvider); + testSseModel(reactiveStreamProvider); + testSseString(reactiveStreamGateway); + testSseModel(reactiveStreamGateway); + } + + private void testSseModel(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseModel(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(Model s) { + buffer.append(s.getName()).append(s.getAge()); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("jack0jack1jack2jack3jack4", buffer.toString()); + } + + private void testSseString(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseString(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(String s) { + buffer.append(s); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("abc", buffer.toString()); + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java new file mode 100644 index 00000000000..26a2a491bbe --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.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.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCaseRunner; +import org.apache.servicecomb.demo.TestMgr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class TestClientApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); + + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args); + + run(); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + } + + public static void run() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java new file mode 100644 index 00000000000..abe96a4f10b --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.localregistry.RegistryBean; +import org.apache.servicecomb.localregistry.RegistryBean.Instance; +import org.apache.servicecomb.localregistry.RegistryBean.Instances; +import org.apache.servicecomb.provider.pojo.Invoker; +import org.reactivestreams.Publisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.WebSocket; + +@Configuration +public class ThirdSvcConfiguration { + @RequestMapping(path = "/ws") + public interface WebsocketClient { + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + WebSocket websocket(); + } + + @RequestMapping(path = "/") + public interface ReactiveStreamClient { + class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + Publisher sseString(); + + @GetMapping("/sseModel") + Publisher sseModel(); + } + + @Bean + public RegistryBean providerServiceBean() { + return new RegistryBean() + .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) + .setAppId("demo-consul") + .setServiceName("provider") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of( + new Instance().setEndpoints(List.of("rest://localhost:9094"))))); + } + + @Bean + public RegistryBean gatewayServiceBean() { + return new RegistryBean() + .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) + .addSchemaInterface("WebsocketController", WebsocketClient.class) + .setAppId("demo-consul") + .setServiceName("gateway") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of( + new Instance().setEndpoints(List.of("rest://localhost:9090?websocketEnabled=true"))))); + } + + @Bean("reactiveStreamProvider") + public ReactiveStreamClient reactiveStreamProvider() { + return Invoker.createProxy("provider", "ReactiveStreamController", ReactiveStreamClient.class); + } + + @Bean("reactiveStreamGateway") + public ReactiveStreamClient reactiveStreamGateway() { + return Invoker.createProxy("gateway", "ReactiveStreamController", ReactiveStreamClient.class); + } + + @Bean + public WebsocketClient gatewayWebsocketClient() { + return Invoker.createProxy("gateway", "WebsocketController", WebsocketClient.class); + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/User.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/User.java new file mode 100644 index 00000000000..ff421930ef2 --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/User.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.servicecomb.samples; + + +public class User { + + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java new file mode 100644 index 00000000000..c36f2d16d28 --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.WebsocketClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import io.vertx.core.http.WebSocket; + +@Component +public class WebsocketIT implements CategorizedTestCase { + @Autowired + private WebsocketClient websocketClient; + + @Override + public void testRestTransport() throws Exception { + StringBuffer sb = new StringBuffer(); + AtomicBoolean closed = new AtomicBoolean(false); + CountDownLatch latchStarted = new CountDownLatch(1); + CountDownLatch latch = new CountDownLatch(1); + + WebSocket webSocket = websocketClient.websocket(); + webSocket.textMessageHandler(s -> { + if ("started".equals(s)) { + latchStarted.countDown(); + return; + } + sb.append(s); + sb.append(" "); + webSocket.writeTextMessage(s); + }); + webSocket.closeHandler(v -> { + closed.set(true); + latch.countDown(); + }); + + webSocket.writeTextMessage("start"); + int i = 0; + for (; i < 10; i++) { + if (!latchStarted.await(3, TimeUnit.SECONDS)) { + webSocket.writeTextMessage("start"); + continue; + } + break; + } + TestMgr.check(i < 10, true); + latch.await(30, TimeUnit.SECONDS); + TestMgr.check(sb.toString(), "hello hello 0 hello 1 hello 2 hello 3 hello 4 total 6 "); + TestMgr.check(closed.get(), true); + } +} diff --git a/demo/demo-consul/test-client/src/main/resources/application.yml b/demo/demo-consul/test-client/src/main/resources/application.yml new file mode 100644 index 00000000000..718fbd9cc84 --- /dev/null +++ b/demo/demo-consul/test-client/src/main/resources/application.yml @@ -0,0 +1,40 @@ +# +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: demo-consul + name: test-client + version: 0.0.1 + + rest: + address: 0.0.0.0:9097 # should be same with server.port to use web container + + config: + consul: + enabled: true + host: 127.0.0.1 + scheme: http + acl-token: '' + instance-tag: tag1 + +test1: env +test2: applition +test3: service +test4: version +test5: tag diff --git a/demo/demo-consul/test-client/src/main/resources/log4j2.xml b/demo/demo-consul/test-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-consul/test-client/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-consul/test-client/src/test/java/org/apache/servicecomb/samples/ConsulIT.java b/demo/demo-consul/test-client/src/test/java/org/apache/servicecomb/samples/ConsulIT.java new file mode 100644 index 00000000000..62862743e27 --- /dev/null +++ b/demo/demo-consul/test-client/src/test/java/org/apache/servicecomb/samples/ConsulIT.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.servicecomb.samples; + +import org.apache.servicecomb.demo.TestMgr; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = TestClientApplication.class) +public class ConsulIT { + private static final Logger LOGGER = LoggerFactory.getLogger(ConsulIT.class); + + @BeforeEach + public void setUp() { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() { + try { + TestClientApplication.run(); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} diff --git a/demo/demo-crossapp/crossapp-client/pom.xml b/demo/demo-crossapp/crossapp-client/pom.xml index cc21f20780f..fa2e051cd0f 100644 --- a/demo/demo-crossapp/crossapp-client/pom.xml +++ b/demo/demo-crossapp/crossapp-client/pom.xml @@ -23,24 +23,12 @@ org.apache.servicecomb.demo demo-crossapp - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT crossapp-client Java Chassis::Demo::CrossApp::Client - - org.apache.servicecomb.demo - demo-schema - - - org.apache.servicecomb - provider-pojo - - - org.apache.servicecomb - provider-springmvc - @@ -54,24 +42,88 @@ crossapp-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-crossapp/crossapp-client/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappClient.java b/demo/demo-crossapp/crossapp-client/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappClient.java index aed0933bea5..7d78648963a 100644 --- a/demo/demo-crossapp/crossapp-client/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappClient.java +++ b/demo/demo-crossapp/crossapp-client/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappClient.java @@ -24,7 +24,6 @@ import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @@ -33,10 +32,11 @@ import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @SpringBootApplication -@EnableServiceComb public class CrossappClient { @RpcReference(microserviceName = "appServer:appService", schemaId = "helloworld") private static HelloWorld helloWorld; @@ -47,14 +47,14 @@ public static void main(String[] args) throws Exception { run(); } - @SuppressWarnings({"deprecation"}) public static void run() { System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); - Object result = InvokerUtils.syncInvoke("appServer:appService", "helloworld", "sayHello", null); + String result = InvokerUtils.syncInvoke("appServer:appService", "helloworld", + "sayHello", null, String.class); TestMgr.check("hello world", result); - RestTemplate restTemplate = RestTemplateBuilder.create(); + RestOperations restTemplate = RestTemplateBuilder.create(); result = restTemplate.getForObject("cse://appServer:appService/helloworld/hello", String.class); TestMgr.check("hello world", result); result = restTemplate.getForObject("servicecomb://appServer:appService/helloworld/hello", String.class); @@ -63,28 +63,84 @@ public static void run() { result = helloWorld.sayHello(); TestMgr.check("hello world", result); - testCorsHandler(); + testCorsHandlerOptions(); + testCorsHandlerGet(); TestMgr.summary(); System.setProperty("sun.net.http.allowRestrictedHeaders", "false"); } - private static void testCorsHandler() { - RestTemplate springRestTemplate = new RestTemplate(); + private static void testCorsHandlerOptions() { + // first domain + RestOperations springRestTemplate = new RestTemplate(); MultiValueMap requestHeaders = new LinkedMultiValueMap<>(); - requestHeaders.put("Origin", Collections.singletonList("http://localhost:8080")); + requestHeaders.put("Origin", Collections.singletonList("http://test.domain:8080")); requestHeaders.put("Access-Control-Request-Method", Collections.singletonList("PUT")); - HttpEntity requestEntity = new HttpEntity<>(requestHeaders); ResponseEntity responseEntity = springRestTemplate .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.OPTIONS, requestEntity, String.class); - - TestMgr.check("204", responseEntity.getStatusCodeValue()); + TestMgr.check("204", responseEntity.getStatusCode().value()); TreeSet sortedSet = new TreeSet<>(responseEntity.getHeaders().get("Access-Control-Allow-Methods")); TestMgr.check("[DELETE,POST,GET,PUT]", sortedSet); sortedSet = new TreeSet<>(responseEntity.getHeaders().get("Access-Control-Allow-Headers")); TestMgr.check("[abc,def]", sortedSet); - TestMgr.check("*", responseEntity.getHeaders().getFirst("Access-Control-Allow-Origin")); + TestMgr.check("http://test.domain:8080", + responseEntity.getHeaders().getFirst("Access-Control-Allow-Origin")); + + // second domain + requestHeaders = new LinkedMultiValueMap<>(); + requestHeaders.put("Origin", Collections.singletonList("http://test.domain:9090")); + requestHeaders.put("Access-Control-Request-Method", Collections.singletonList("PUT")); + requestEntity = new HttpEntity<>(requestHeaders); + responseEntity = springRestTemplate + .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.OPTIONS, requestEntity, + String.class); + TestMgr.check("204", responseEntity.getStatusCode().value()); + sortedSet = new TreeSet<>(responseEntity.getHeaders().get("Access-Control-Allow-Methods")); + TestMgr.check("[DELETE,POST,GET,PUT]", sortedSet); + sortedSet = new TreeSet<>(responseEntity.getHeaders().get("Access-Control-Allow-Headers")); + TestMgr.check("[abc,def]", sortedSet); + TestMgr.check("http://test.domain:9090", + responseEntity.getHeaders().getFirst("Access-Control-Allow-Origin")); + } + + private static void testCorsHandlerGet() { + // allowed origin + RestOperations springRestTemplate = new RestTemplate(); + MultiValueMap requestHeaders = new LinkedMultiValueMap<>(); + requestHeaders.put("Origin", Collections.singletonList("http://test.domain:8080")); + HttpEntity requestEntity = new HttpEntity<>(requestHeaders); + ResponseEntity responseEntity = springRestTemplate + .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.GET, requestEntity, + String.class); + + TestMgr.check("200", responseEntity.getStatusCode().value()); + TestMgr.check("hello world", responseEntity.getBody()); + + // allowed origin + requestHeaders = new LinkedMultiValueMap<>(); + requestHeaders.put("Origin", Collections.singletonList("http://test.domain:9090")); + requestEntity = new HttpEntity<>(requestHeaders); + responseEntity = springRestTemplate + .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.GET, requestEntity, + String.class); + + TestMgr.check("200", responseEntity.getStatusCode().value()); + TestMgr.check("hello world", responseEntity.getBody()); + + // not allowed origin + try { + requestHeaders = new LinkedMultiValueMap<>(); + requestHeaders.put("Origin", Collections.singletonList("http://test.domain:7070")); + requestEntity = new HttpEntity<>(requestHeaders); + springRestTemplate + .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.GET, requestEntity, + String.class); + TestMgr.fail("must throw"); + } catch (HttpServerErrorException e) { + TestMgr.check(500, e.getStatusCode().value()); + TestMgr.check(true, e.getMessage().contains("500 CORS Rejected")); + } } } diff --git a/demo/demo-crossapp/crossapp-client/src/main/resources/microservice.yaml b/demo/demo-crossapp/crossapp-client/src/main/resources/microservice.yaml index 4590f8f8791..1728b196574 100644 --- a/demo/demo-crossapp/crossapp-client/src/main/resources/microservice.yaml +++ b/demo/demo-crossapp/crossapp-client/src/main/resources/microservice.yaml @@ -14,14 +14,13 @@ ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- - -APPLICATION_ID: appClient -service_description: - name: appClientService - version: 0.0.1 servicecomb: service: - registry: + application: appClient + name: appClientService + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 handler: chain: diff --git a/demo/demo-crossapp/crossapp-client/src/main/resources/schemas/helloworld.yaml b/demo/demo-crossapp/crossapp-client/src/main/resources/schemas/helloworld.yaml deleted file mode 100644 index 4a98f862cb1..00000000000 --- a/demo/demo-crossapp/crossapp-client/src/main/resources/schemas/helloworld.yaml +++ /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. -## --------------------------------------------------------------------------- - -swagger: '2.0' -info: - title: hello service - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.crossapp.HelloWorld - -basePath: /helloworld -produces: - - application/json - -paths: - /hello: - get: - operationId: sayHello - responses: - "200": - description: say hello - schema: - type: string diff --git a/demo/demo-crossapp/crossapp-server/pom.xml b/demo/demo-crossapp/crossapp-server/pom.xml index db4b7a525c9..0207bdb7b20 100644 --- a/demo/demo-crossapp/crossapp-server/pom.xml +++ b/demo/demo-crossapp/crossapp-server/pom.xml @@ -23,20 +23,12 @@ org.apache.servicecomb.demo demo-crossapp - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT crossapp-server Java Chassis::Demo::CrossApp::Server - - org.apache.servicecomb.demo - demo-schema - - - org.apache.servicecomb - provider-pojo - @@ -55,28 +47,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappBootListener.java b/demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappBootListener.java deleted file mode 100644 index fa8202b7a5c..00000000000 --- a/demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappBootListener.java +++ /dev/null @@ -1,29 +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.servicecomb.demo.crossapp; - -import org.apache.servicecomb.core.BootListener; -import org.springframework.stereotype.Component; - -@Component -public class CrossappBootListener implements BootListener { - @Override - public void onBeforeProducerProvider(BootEvent event) { - event.getScbEngine().getSwaggerLoader().registerSwaggersInLocation("schemas"); - } -} diff --git a/demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappServer.java b/demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappServer.java index f8a644a14e7..63678f2e9d6 100644 --- a/demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappServer.java +++ b/demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappServer.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.demo.crossapp; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class CrossappServer { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(CrossappServer.class).web(WebApplicationType.NONE).run(args); diff --git a/demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/HelloWorldImpl.java b/demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/HelloWorldImpl.java index 00051c314c1..b8ede3eba7e 100644 --- a/demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/HelloWorldImpl.java +++ b/demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/HelloWorldImpl.java @@ -17,10 +17,16 @@ package org.apache.servicecomb.demo.crossapp; -import org.apache.servicecomb.provider.pojo.RpcSchema; +import org.apache.servicecomb.provider.rest.common.RestSchema; -@RpcSchema(schemaId = "helloworld") +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +@RestSchema(schemaId = "helloworld") +@Path("helloworld") public class HelloWorldImpl { + @GET + @Path("hello") public String sayHello() { return "hello world"; } diff --git a/demo/demo-crossapp/crossapp-server/src/main/resources/microservice.yaml b/demo/demo-crossapp/crossapp-server/src/main/resources/microservice.yaml index 3f263b861bd..0f1bbdc8853 100644 --- a/demo/demo-crossapp/crossapp-server/src/main/resources/microservice.yaml +++ b/demo/demo-crossapp/crossapp-server/src/main/resources/microservice.yaml @@ -15,25 +15,22 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: appServer -service_description: - name: appService - version: 0.0.1 - properties: - allowCrossApp: true -instance_description: - properties: - tag0: value0 - servicecomb: service: - registry: + application: appServer + name: appService + version: 0.0.1 + properties: + allowCrossApp: true + tag0: value0 + registry: + sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 cors: enabled: true - origin: "*" + origin: "http://test.domain:8080,http://test.domain:9090" allowedHeader: abc,def allowedMethod: GET,PUT,POST,DELETE exposedHeader: abc,def diff --git a/demo/demo-crossapp/crossapp-server/src/main/resources/schemas/helloworld.yaml b/demo/demo-crossapp/crossapp-server/src/main/resources/schemas/helloworld.yaml deleted file mode 100644 index 4a98f862cb1..00000000000 --- a/demo/demo-crossapp/crossapp-server/src/main/resources/schemas/helloworld.yaml +++ /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. -## --------------------------------------------------------------------------- - -swagger: '2.0' -info: - title: hello service - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.crossapp.HelloWorld - -basePath: /helloworld -produces: - - application/json - -paths: - /hello: - get: - operationId: sayHello - responses: - "200": - description: say hello - schema: - type: string diff --git a/demo/demo-crossapp/pom.xml b/demo/demo-crossapp/pom.xml index e9f7eb5e04b..08c2055d357 100644 --- a/demo/demo-crossapp/pom.xml +++ b/demo/demo-crossapp/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-crossapp Java Chassis::Demo::CrossApp @@ -34,6 +34,10 @@ + + org.apache.servicecomb.demo + demo-schema + org.apache.servicecomb registry-service-center diff --git a/demo/demo-cse-v1/consumer/pom.xml b/demo/demo-cse-v1/consumer/pom.xml index 63fca453f6b..78a7bf75a23 100644 --- a/demo/demo-cse-v1/consumer/pom.xml +++ b/demo/demo-cse-v1/consumer/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-cse-v1 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v1-consumer @@ -44,4 +44,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java b/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java index 74068d30345..3e7a2a0e285 100644 --- a/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java +++ b/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.samples; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class ConsumerApplication { public static void main(String[] args) throws Exception { try { diff --git a/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.java b/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.java index 6777fb7a190..f86c5a29492 100644 --- a/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.java +++ b/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.java @@ -26,8 +26,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import com.netflix.config.DynamicPropertyFactory; - @RestSchema(schemaId = "ConsumerConfigController") @RequestMapping(path = "/") public class ConsumerConfigController { @@ -36,7 +34,8 @@ public class ConsumerConfigController { private ConsumerConfigurationProperties consumerConfigurationProperties; @Autowired - public ConsumerConfigController(Environment environment, ConsumerConfigurationProperties consumerConfigurationProperties) { + public ConsumerConfigController(Environment environment, + ConsumerConfigurationProperties consumerConfigurationProperties) { this.environment = environment; this.consumerConfigurationProperties = consumerConfigurationProperties; } @@ -58,18 +57,12 @@ public String bar() { @GetMapping("/dynamicString") public String dynamicString(@RequestParam("key") String key) { - return DynamicPropertyFactory.getInstance().getStringProperty(key, null).get(); + return environment.getProperty(key); } @GetMapping("/dynamicArray") @SuppressWarnings("unchecked") public List dynamicArray() { return consumerConfigurationProperties.getDynamicArray(); -// DynamicPropertyFactory & Environment do not support arrays like: -// key[0]: v0 -// key[1]: v1 -// return environment.getProperty(key, List.class); -// return Arrays.asList(((AbstractConfiguration) DynamicPropertyFactory.getBackingConfigurationSource()) -// .getStringArray(key)); } } diff --git a/demo/demo-cse-v1/consumer/src/main/resources/application.yml b/demo/demo-cse-v1/consumer/src/main/resources/application.yml index acc9b81f715..11f739de444 100644 --- a/demo/demo-cse-v1/consumer/src/main/resources/application.yml +++ b/demo/demo-cse-v1/consumer/src/main/resources/application.yml @@ -24,10 +24,10 @@ servicecomb: application: demo-java-chassis-cse-v1 name: consumer version: 0.0.1 - registry: + registry: + sc: address: https://cse.cn-south-1.myhuaweicloud.com - instance: - watch: false + watch: false config: client: serverUri: https://cse.cn-south-1.myhuaweicloud.com @@ -59,6 +59,71 @@ servicecomb: - weight: 100 tags: version: 0.0.1 + - precedence: 3 + match: + headers: + canary: + exact: fallback + route: + - weight: 100 + tags: + version: 0.0.3 + fallback: + - weight: 20 + tags: + version: 0.0.1 + - weight: 80 + tags: + version: 0.0.2 + - precedence: 4 + emptyProtection: false + match: + headers: + canary: + exact: emptyProtectionClose100 + route: + - weight: 100 + tags: + version: 0.0.3 + - precedence: 5 + emptyProtection: false + match: + headers: + canary: + exact: emptyProtectionCloseLess100 + route: + - weight: 50 + tags: + version: 0.0.3 + - precedence: 6 + emptyProtection: false + match: + headers: + canary: + exact: emptyProtectionCloseFallback + route: + - weight: 100 + tags: + version: 0.0.3 + fallback: + - weight: 100 + tags: + version: 0.0.1 + - precedence: 7 + emptyProtection: false + match: + headers: + canary: + exact: emptyProtectionClose100-2 + route: + - weight: 50 + tags: + version: 0.0.1 + - weight: 50 + tags: + version: 0.0.3 + + router: type: router header: canary @@ -70,5 +135,3 @@ servicecomb: secretKey: ${CREDENTIALS_SK} akskCustomCipher: default project: cn-south-1 - - diff --git a/demo/demo-cse-v1/consumer/src/main/resources/log4j2.xml b/demo/demo-cse-v1/consumer/src/main/resources/log4j2.xml index 313d1fcec1f..4e54783856a 100644 --- a/demo/demo-cse-v1/consumer/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v1/consumer/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/gateway/pom.xml b/demo/demo-cse-v1/gateway/pom.xml index b130e1ed8bc..49241e669cf 100644 --- a/demo/demo-cse-v1/gateway/pom.xml +++ b/demo/demo-cse-v1/gateway/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-cse-v1 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v1-gateway @@ -47,4 +47,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java b/demo/demo-cse-v1/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java index 3f102607094..7d58caafd9d 100644 --- a/demo/demo-cse-v1/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java +++ b/demo/demo-cse-v1/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.samples; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class GatewayApplication { public static void main(String[] args) throws Exception { try { diff --git a/demo/demo-cse-v1/gateway/src/main/resources/application.yml b/demo/demo-cse-v1/gateway/src/main/resources/application.yml index 9f8b8c0b8ef..52f065c4f62 100644 --- a/demo/demo-cse-v1/gateway/src/main/resources/application.yml +++ b/demo/demo-cse-v1/gateway/src/main/resources/application.yml @@ -24,10 +24,10 @@ servicecomb: application: demo-java-chassis-cse-v1 name: gateway version: 0.0.1 - registry: + registry: + sc: address: https://cse.cn-south-1.myhuaweicloud.com - instance: - watch: false + watch: false config: client: serverUri: https://cse.cn-south-1.myhuaweicloud.com @@ -50,9 +50,16 @@ servicecomb: microserviceName: consumer versionRule: 0.0.0+ - router: - type: router - header: canary + matchGroup: + canary-header: | + matches: + - headers: + canary: + prefix: "" + mapper: + canary-header: | + target: + canary: $H{canary} # Configure AK/SK credentials if needed. Default not enabled. credentials: @@ -60,4 +67,4 @@ servicecomb: accessKey: ${CREDENTIALS_AK} secretKey: ${CREDENTIALS_SK} akskCustomCipher: default - project: cn-south-1 \ No newline at end of file + project: cn-south-1 diff --git a/demo/demo-cse-v1/gateway/src/main/resources/log4j2.xml b/demo/demo-cse-v1/gateway/src/main/resources/log4j2.xml index e9bb2656f8f..165cdf7e0a3 100644 --- a/demo/demo-cse-v1/gateway/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v1/gateway/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/pom.xml b/demo/demo-cse-v1/pom.xml index a8f3a77a724..72e70e58c15 100644 --- a/demo/demo-cse-v1/pom.xml +++ b/demo/demo-cse-v1/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-cse-v1 Java Chassis::Demo::CSE-V1 @@ -47,7 +47,23 @@ org.apache.servicecomb - servicestage-environment + config-cc + + + org.apache.servicecomb + servicestage + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core @@ -59,4 +75,4 @@ test-client - \ No newline at end of file + diff --git a/demo/demo-cse-v1/provider-canary/pom.xml b/demo/demo-cse-v1/provider-canary/pom.xml index d1d0726857d..34c393b9e8d 100644 --- a/demo/demo-cse-v1/provider-canary/pom.xml +++ b/demo/demo-cse-v1/provider-canary/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-cse-v1 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v1-provider-canary @@ -48,4 +48,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/provider-canary/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java b/demo/demo-cse-v1/provider-canary/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java index f288353e66c..fde6f36b2e3 100644 --- a/demo/demo-cse-v1/provider-canary/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java +++ b/demo/demo-cse-v1/provider-canary/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.samples; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class ProviderApplication { public static void main(String[] args) throws Exception { try { diff --git a/demo/demo-cse-v1/provider-canary/src/main/resources/application.yml b/demo/demo-cse-v1/provider-canary/src/main/resources/application.yml index 81bf7aaffb7..29e8e8f3879 100644 --- a/demo/demo-cse-v1/provider-canary/src/main/resources/application.yml +++ b/demo/demo-cse-v1/provider-canary/src/main/resources/application.yml @@ -23,10 +23,10 @@ servicecomb: application: demo-java-chassis-cse-v1 name: provider version: 0.0.2 - registry: + registry: + sc: address: https://cse.cn-south-1.myhuaweicloud.com - instance: - watch: false + watch: false config: client: serverUri: https://cse.cn-south-1.myhuaweicloud.com diff --git a/demo/demo-cse-v1/provider-canary/src/main/resources/log4j2.xml b/demo/demo-cse-v1/provider-canary/src/main/resources/log4j2.xml index 56fc9a0a676..9ac74305e0c 100644 --- a/demo/demo-cse-v1/provider-canary/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v1/provider-canary/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/provider/pom.xml b/demo/demo-cse-v1/provider/pom.xml index cfc6fe55af4..12015bd6b7a 100644 --- a/demo/demo-cse-v1/provider/pom.xml +++ b/demo/demo-cse-v1/provider/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-cse-v1 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v1-provider @@ -48,4 +48,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java b/demo/demo-cse-v1/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java index f288353e66c..fde6f36b2e3 100644 --- a/demo/demo-cse-v1/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java +++ b/demo/demo-cse-v1/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.samples; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class ProviderApplication { public static void main(String[] args) throws Exception { try { diff --git a/demo/demo-cse-v1/provider/src/main/resources/application.yml b/demo/demo-cse-v1/provider/src/main/resources/application.yml index 370c3cd975e..097e2e9cc85 100644 --- a/demo/demo-cse-v1/provider/src/main/resources/application.yml +++ b/demo/demo-cse-v1/provider/src/main/resources/application.yml @@ -23,10 +23,10 @@ servicecomb: application: demo-java-chassis-cse-v1 name: provider version: 0.0.1 - registry: + registry: + sc: address: https://cse.cn-south-1.myhuaweicloud.com - instance: - watch: false + watch: false config: client: serverUri: https://cse.cn-south-1.myhuaweicloud.com diff --git a/demo/demo-cse-v1/provider/src/main/resources/log4j2.xml b/demo/demo-cse-v1/provider/src/main/resources/log4j2.xml index 56fc9a0a676..9ac74305e0c 100644 --- a/demo/demo-cse-v1/provider/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v1/provider/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/test-client/pom.xml b/demo/demo-cse-v1/test-client/pom.xml index 3996dd2f7d6..697c28fb64e 100644 --- a/demo/demo-cse-v1/test-client/pom.xml +++ b/demo/demo-cse-v1/test-client/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-cse-v1 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v1-test-client @@ -52,4 +52,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java index 30200b65160..2aff8c25966 100644 --- a/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java +++ b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java @@ -22,11 +22,12 @@ import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class ConsumerConfigIT implements CategorizedTestCase { - RestTemplate template = new RestTemplate(); + RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { @@ -37,11 +38,11 @@ public void testRestTransport() throws Exception { @SuppressWarnings("unchecked") private void testConfig() { String result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v1.test.foo", String.class); - TestMgr.check("\"foo\"", result); + TestMgr.check("foo", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v1.test.bar", String.class); - TestMgr.check("\"bar\"", result); + TestMgr.check("bar", result); result = template.getForObject(Config.GATEWAY_URL + "/dynamicString?key=cse.v1.test.dynamicString", String.class); - TestMgr.check("\"a,b\"", result); + TestMgr.check("a,b", result); List listResult = template .getForObject(Config.GATEWAY_URL + "/dynamicArray", List.class); @@ -52,8 +53,8 @@ private void testConfig() { private void testFooBar() { String result = template.getForObject(Config.GATEWAY_URL + "/foo", String.class); - TestMgr.check("\"foo\"", result); + TestMgr.check("foo", result); result = template.getForObject(Config.GATEWAY_URL + "/bar", String.class); - TestMgr.check("\"bar\"", result); + TestMgr.check("bar", result); } } diff --git a/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java index 124df7712ab..691ee47a162 100644 --- a/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java +++ b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java @@ -24,17 +24,24 @@ import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class HelloWorldIT implements CategorizedTestCase { - RestTemplate template = new RestTemplate(); + RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { + testHelloWorldFallback(); + testHelloWorldNoHeader(); testHelloWorld(); testHelloWorldCanary(); + testHelloWorldEmptyProtectionCloseWeight100(); + testHelloWorldeEmptyProtectionCloseWeightLess100(); + testHelloWorldEmptyProtectionCloseFallback(); + testHelloWorldEmptyProtectionCloseWeight100Two(); } private void testHelloWorld() { @@ -44,7 +51,7 @@ private void testHelloWorld() { HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/sayHello?name=World", HttpMethod.GET, entity, String.class).getBody(); - TestMgr.check("\"Hello World\"", result); + TestMgr.check("Hello World", result); } } @@ -56,6 +63,30 @@ private void testHelloWorldCanary() { MultiValueMap headers = new HttpHeaders(); headers.add("canary", "new"); HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, entity, String.class).getBody(); + if (result.equals("Hello Canary World")) { + oldCount++; + } else if (result.equals("Hello Canary in canary World")) { + newCount++; + } else { + TestMgr.fail("not expected result testHelloWorldCanary"); + return; + } + } + + double ratio = oldCount / (float) (oldCount + newCount); + TestMgr.check(ratio > 0.1 && ratio < 0.3, true); + } + + private void testHelloWorldFallback() { + int oldCount = 0; + int newCount = 0; + + for (int i = 0; i < 20; i++) { + MultiValueMap headers = new HttpHeaders(); + headers.add("canary", "fallback"); + HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, entity, String.class).getBody(); if (result.equals("\"Hello Canary World\"")) { @@ -69,6 +100,105 @@ private void testHelloWorldCanary() { } double ratio = oldCount / (float) (oldCount + newCount); - TestMgr.check(ratio > 0.1 && ratio < 0.3, true); + TestMgr.check(Math.abs(ratio - 0.2) <= 0.1, true); + } + + private void testHelloWorldNoHeader() { + int oldCount = 0; + int newCount = 0; + + for (int i = 0; i < 20; i++) { + String result = template + .getForObject(Config.GATEWAY_URL + "/sayHelloCanary?name=World", String.class); + if (result.equals("\"Hello Canary World\"")) { + oldCount++; + } else if (result.equals("\"Hello Canary in canary World\"")) { + newCount++; + } else { + TestMgr.fail("not expected result testHelloWorldCanary"); + return; + } + } + + double ratio = oldCount / (float) (oldCount + newCount); + TestMgr.check(Double.compare(ratio, 0.5) == 0, true); + } + + private void testHelloWorldEmptyProtectionCloseWeight100() { + int failCount = 0; + + for (int i = 0; i < 20; i++) { + MultiValueMap headers = new HttpHeaders(); + headers.add("canary", "emptyProtectionClose100"); + HttpEntity entity = new HttpEntity<>(headers); + try { + template.exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, + entity, String.class); + } catch (Exception e) { + failCount++; + } + } + + TestMgr.check(failCount == 20, true); + } + + private void testHelloWorldeEmptyProtectionCloseWeightLess100() { + int failCount = 0; + int succCount = 0; + + for (int i = 0; i < 20; i++) { + MultiValueMap headers = new HttpHeaders(); + headers.add("canary", "emptyProtectionCloseLess100"); + HttpEntity entity = new HttpEntity<>(headers); + try { + template.exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, + entity, String.class); + succCount++; + } catch (Exception e) { + failCount++; + } + } + + TestMgr.check(succCount == 20, true); + } + + private void testHelloWorldEmptyProtectionCloseFallback() { + int failCount = 0; + int succCount = 0; + + for (int i = 0; i < 20; i++) { + MultiValueMap headers = new HttpHeaders(); + headers.add("canary", "emptyProtectionCloseFallback"); + HttpEntity entity = new HttpEntity<>(headers); + try { + template.exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, + entity, String.class); + succCount++; + } catch (Exception e) { + failCount++; + } + } + + TestMgr.check(succCount == 20, true); + } + + private void testHelloWorldEmptyProtectionCloseWeight100Two() { + int failCount = 0; + int succCount = 0; + + for (int i = 0; i < 20; i++) { + MultiValueMap headers = new HttpHeaders(); + headers.add("canary", "emptyProtectionClose100-2"); + HttpEntity entity = new HttpEntity<>(headers); + try { + template.exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, + entity, String.class); + succCount++; + } catch (Exception e) { + failCount++; + } + } + + TestMgr.check(failCount == succCount, true); } } diff --git a/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java index b18a13337bb..10d62c6bba4 100644 --- a/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java +++ b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java @@ -19,7 +19,6 @@ import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; @@ -27,7 +26,6 @@ import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class TestClientApplication { private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); diff --git a/demo/demo-cse-v1/test-client/src/main/resources/application.yml b/demo/demo-cse-v1/test-client/src/main/resources/application.yml index 63c28aa2d49..13ffddc60b8 100644 --- a/demo/demo-cse-v1/test-client/src/main/resources/application.yml +++ b/demo/demo-cse-v1/test-client/src/main/resources/application.yml @@ -23,10 +23,10 @@ servicecomb: application: demo-java-chassis-cse-v1 name: test-client version: 0.0.1 - registry: + registry: + sc: address: https://cse.cn-south-1.myhuaweicloud.com - instance: - watch: false + watch: false config: client: serverUri: https://cse.cn-south-1.myhuaweicloud.com @@ -40,4 +40,4 @@ servicecomb: accessKey: ${CREDENTIALS_AK} secretKey: ${CREDENTIALS_SK} akskCustomCipher: default - project: cn-south-1 \ No newline at end of file + project: cn-south-1 diff --git a/demo/demo-cse-v1/test-client/src/main/resources/log4j2.xml b/demo/demo-cse-v1/test-client/src/main/resources/log4j2.xml index 56fc9a0a676..9ac74305e0c 100644 --- a/demo/demo-cse-v1/test-client/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v1/test-client/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/README.md b/demo/demo-cse-v2/README.md index 7d6b20b4791..dda7dee702e 100644 --- a/demo/demo-cse-v2/README.md +++ b/demo/demo-cse-v2/README.md @@ -9,7 +9,7 @@ * 依次启动 provider、consumer、gateway * 在配置中心增加如下配置: -* 应用级配置:consumer.yaml。类型为 yaml。 +* 应用级配置:consumer.yaml。类型为 yaml。 ```yaml cse: @@ -18,7 +18,7 @@ cse: foo: foo ``` - * 自定义配置:priority1.yaml。label信息: public=default 。类型为 yaml。 + * 自定义配置:priority1.yaml。label信息: public=default 。类型为 yaml。 ```yaml cse: v2: @@ -27,7 +27,7 @@ cse: common: common ``` - * 自定义配置:priority1.yaml。label信息: public=default,extra=default 。类型为 yaml。 + * 自定义配置:priority1.yaml。label信息: public=default,extra=default 。类型为 yaml。 ```yaml cse: v2: @@ -36,7 +36,7 @@ cse: extra: common ``` - * 应用级配置:priority2.yaml。类型为 yaml。 + * 应用级配置:priority2.yaml。类型为 yaml。 ```yaml cse: v2: @@ -44,7 +44,7 @@ cse: priority: v2 ``` - * 服务级配置:priority3.yaml,微服务性选择consumer。类型为 yaml。 + * 服务级配置:priority3.yaml,微服务性选择consumer。类型为 yaml。 ```yaml cse: v2: @@ -52,13 +52,6 @@ cse: priority: v3 ``` - * 自定义配置:priority3.yaml,labels: app=demo-java-chassis-cse-v2,environment=,service=consumer,extra=。类型为 yaml。 -```yaml -cse: - v2: - test: - priority: v4 -``` * 应用级配置:consumerApp.yaml,应用选择demo-java-chassis-cse-v2。类型为 yaml。 ```yaml cse: @@ -66,27 +59,21 @@ cse: test: priority1: v1 ``` -* 服务级配置:consumerSer.yaml,微服务性选择consumer。类型为 yaml。 + +* 服务级配置:consumerService.yaml,微服务性选择consumer。类型为 yaml。 ```yaml cse: v2: test: priority1: v2 ``` -* 版本级配置:consumerIns.yaml,labels: app=demo-java-chassis-cse-v2,environment=,service=consumer,version = 0.0.1。类型为 yaml。 -```yaml -cse: - v2: - test: - priority1: v3 -``` - * 应用级配置: cse.v2.test.bar: bar 。 类型为 text。 - + * 应用级配置: cse.v2.test.bar: bar 。 类型为 text。 + * 执行 tests-client 里面的集成测试用例 (成功) * 修改 - * priority1.yaml。label信息: public=default 。类型为 yaml。 + * priority1.yaml。label信息: public=default 。类型为 yaml。 ```yaml cse: v2: @@ -94,10 +81,10 @@ cse: priority: v4 ``` -* 执行 tests-client 里面的集成测试用例 (成功) +* 执行 tests-client 里面的集成测试用例 (成功) * 修改 - * 应用级priority3.yaml。 + * 应用级priority3.yaml。 ```yaml cse: v2: @@ -105,10 +92,10 @@ cse: priority: v5 ``` -* 执行 tests-client 里面的集成测试用例 (失败) +* 执行 tests-client 里面的集成测试用例 (失败) * 修改 - * 应用级priority3.yaml。label信息: + * 应用级priority3.yaml。label信息: ```yaml cse: v2: @@ -116,7 +103,7 @@ cse: priority: v3 ``` -* 执行 tests-client 里面的集成测试用例 (成功) +* 执行 tests-client 里面的集成测试用例 (成功) * 修改 * 应用级consumerApp.yaml。 ```yaml @@ -126,9 +113,9 @@ cse: priority1: v10 ``` -* 执行 tests-client 里面的集成测试用例 (成功) +* 执行 tests-client 里面的集成测试用例 (成功) * 修改 - * 服务级配置:consumerSer.yaml。 + * 服务级配置:consumerService.yaml。 ```yaml cse: v2: diff --git a/demo/demo-cse-v2/consumer/pom.xml b/demo/demo-cse-v2/consumer/pom.xml index ed18ec7ddbd..0fe6e05f4d0 100644 --- a/demo/demo-cse-v2/consumer/pom.xml +++ b/demo/demo-cse-v2/consumer/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-cse-v2 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v2-consumer @@ -44,4 +44,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java b/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java index 74068d30345..3e7a2a0e285 100644 --- a/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java +++ b/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.samples; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class ConsumerApplication { public static void main(String[] args) throws Exception { try { diff --git a/demo/demo-cse-v2/consumer/src/main/resources/application.yml b/demo/demo-cse-v2/consumer/src/main/resources/application.yml index 72a6dafadb5..5982bb668b5 100644 --- a/demo/demo-cse-v2/consumer/src/main/resources/application.yml +++ b/demo/demo-cse-v2/consumer/src/main/resources/application.yml @@ -24,15 +24,14 @@ servicecomb: application: demo-java-chassis-cse-v2 name: consumer version: 0.0.1 - registry: - address: ${CSE_V2_SC} - instance: - watch: false + registry: + sc: + address: ${PAAS_CSE_SC_ENDPOINT:http://127.0.0.1:30100} + watch: false kie: - serverUri: ${CSE_V2_CC} + serverUri: ${PAAS_CSE_CC_ENDPOINT:http://127.0.0.1:30110} customLabel: public customLabelValue: default rest: address: 0.0.0.0:9092 # should be same with server.port to use web container - diff --git a/demo/demo-cse-v2/consumer/src/main/resources/log4j2.xml b/demo/demo-cse-v2/consumer/src/main/resources/log4j2.xml index 313d1fcec1f..4e54783856a 100644 --- a/demo/demo-cse-v2/consumer/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v2/consumer/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/gateway/pom.xml b/demo/demo-cse-v2/gateway/pom.xml index d71ad70073d..1d04a248410 100644 --- a/demo/demo-cse-v2/gateway/pom.xml +++ b/demo/demo-cse-v2/gateway/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-cse-v2 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v2-gateway @@ -47,4 +47,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java b/demo/demo-cse-v2/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java index 3f102607094..7d58caafd9d 100644 --- a/demo/demo-cse-v2/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java +++ b/demo/demo-cse-v2/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.samples; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class GatewayApplication { public static void main(String[] args) throws Exception { try { diff --git a/demo/demo-cse-v2/gateway/src/main/resources/application.yml b/demo/demo-cse-v2/gateway/src/main/resources/application.yml index 07e25faa4a2..32d2a728d76 100644 --- a/demo/demo-cse-v2/gateway/src/main/resources/application.yml +++ b/demo/demo-cse-v2/gateway/src/main/resources/application.yml @@ -24,10 +24,11 @@ servicecomb: application: demo-java-chassis-cse-v2 name: gateway version: 0.0.1 - registry: + registry: + sc: address: ${PAAS_CSE_SC_ENDPOINT:http://127.0.0.1:30100} - instance: - watch: false + watch: false + kie: serverUri: ${PAAS_CSE_CC_ENDPOINT:http://127.0.0.1:30110} customLabel: public diff --git a/demo/demo-cse-v2/gateway/src/main/resources/log4j2.xml b/demo/demo-cse-v2/gateway/src/main/resources/log4j2.xml index e9bb2656f8f..165cdf7e0a3 100644 --- a/demo/demo-cse-v2/gateway/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v2/gateway/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/pom.xml b/demo/demo-cse-v2/pom.xml index 83dcbe35c9b..35a623930da 100644 --- a/demo/demo-cse-v2/pom.xml +++ b/demo/demo-cse-v2/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-cse-v2 Java Chassis::Demo::CSE-V2 @@ -39,7 +39,19 @@ org.apache.servicecomb - servicestage-environment + config-kie + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core @@ -50,4 +62,4 @@ test-client - \ No newline at end of file + diff --git a/demo/demo-cse-v2/provider/pom.xml b/demo/demo-cse-v2/provider/pom.xml index 5dcc140d22a..4308b1169d9 100644 --- a/demo/demo-cse-v2/provider/pom.xml +++ b/demo/demo-cse-v2/provider/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-cse-v2 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v2-provider @@ -48,4 +48,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java b/demo/demo-cse-v2/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java index f288353e66c..fde6f36b2e3 100644 --- a/demo/demo-cse-v2/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java +++ b/demo/demo-cse-v2/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.samples; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class ProviderApplication { public static void main(String[] args) throws Exception { try { diff --git a/demo/demo-cse-v2/provider/src/main/resources/application.yml b/demo/demo-cse-v2/provider/src/main/resources/application.yml index 991bf6d7de0..bb55de45abc 100644 --- a/demo/demo-cse-v2/provider/src/main/resources/application.yml +++ b/demo/demo-cse-v2/provider/src/main/resources/application.yml @@ -27,14 +27,13 @@ servicecomb: application: demo-java-chassis-cse-v2 name: provider version: 0.0.1 - registry: - address: ${CSE_V2_SC} - instance: - watch: false + registry: + sc: + address: ${PAAS_CSE_SC_ENDPOINT:http://127.0.0.1:30100} + watch: false kie: - serverUri: ${CSE_V2_CC} + serverUri: ${PAAS_CSE_CC_ENDPOINT:http://127.0.0.1:30110} customLabel: public rest: address: 0.0.0.0:9093 # should be same with server.port to use web container - diff --git a/demo/demo-cse-v2/provider/src/main/resources/log4j2.xml b/demo/demo-cse-v2/provider/src/main/resources/log4j2.xml index 56fc9a0a676..9ac74305e0c 100644 --- a/demo/demo-cse-v2/provider/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v2/provider/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/test-client/pom.xml b/demo/demo-cse-v2/test-client/pom.xml index 117d520f87d..78917aa7e5c 100644 --- a/demo/demo-cse-v2/test-client/pom.xml +++ b/demo/demo-cse-v2/test-client/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-cse-v2 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v2-test-client @@ -52,4 +52,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java index 8d783d626e2..16b1388641a 100644 --- a/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java +++ b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java @@ -20,11 +20,12 @@ import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class ConsumerConfigIT implements CategorizedTestCase { - RestTemplate template = new RestTemplate(); + RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { @@ -34,29 +35,29 @@ public void testRestTransport() throws Exception { private void testConfig() { String result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.foo", String.class); - TestMgr.check("\"foo\"", result); + TestMgr.check("foo", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.bar", String.class); - TestMgr.check("\"bar\"", result); + TestMgr.check("bar", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.priority", String.class); - TestMgr.check("\"v3\"", result); + TestMgr.check("v1", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.common", String.class); - TestMgr.check("\"common\"", result); + TestMgr.check("common", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.extra", String.class); - TestMgr.check("\"common\"", result); + TestMgr.check("common", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.priority1", String.class); - TestMgr.check("\"v3\"", result); + TestMgr.check("v2", result); } private void testFooBar() { String result = template.getForObject(Config.GATEWAY_URL + "/bar", String.class); - TestMgr.check("\"bar\"", result); + TestMgr.check("bar", result); result = template.getForObject(Config.GATEWAY_URL + "/foo", String.class); - TestMgr.check("\"foo\"", result); + TestMgr.check("foo", result); result = template.getForObject(Config.GATEWAY_URL + "/priority", String.class); - TestMgr.check("\"v3\"", result); + TestMgr.check("v1", result); result = template.getForObject(Config.GATEWAY_URL + "/common", String.class); - TestMgr.check("\"common\"", result); + TestMgr.check("common", result); result = template.getForObject(Config.GATEWAY_URL + "/extra", String.class); - TestMgr.check("\"common\"", result); + TestMgr.check("common", result); } } diff --git a/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java index bce14568675..59d06bb1e01 100644 --- a/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java +++ b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java @@ -20,11 +20,12 @@ import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class HelloWorldIT implements CategorizedTestCase { - RestTemplate template = new RestTemplate(); + RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { @@ -34,6 +35,6 @@ public void testRestTransport() throws Exception { private void testHelloWorld() { String result = template .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); - TestMgr.check("\"Hello World\"", result); + TestMgr.check("Hello World", result); } } diff --git a/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java index b18a13337bb..10d62c6bba4 100644 --- a/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java +++ b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java @@ -19,7 +19,6 @@ import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; @@ -27,7 +26,6 @@ import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class TestClientApplication { private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); diff --git a/demo/demo-cse-v2/test-client/src/main/resources/application.yml b/demo/demo-cse-v2/test-client/src/main/resources/application.yml index 3ab2fb08b37..046fa53ee6b 100644 --- a/demo/demo-cse-v2/test-client/src/main/resources/application.yml +++ b/demo/demo-cse-v2/test-client/src/main/resources/application.yml @@ -27,10 +27,10 @@ servicecomb: application: demo-java-chassis-cse-v2 name: test-client version: 0.0.1 - registry: + registry: + sc: address: ${PAAS_CSE_SC_ENDPOINT:http://127.0.0.1:30100} - instance: - watch: false + watch: false kie: serverUri: ${PAAS_CSE_CC_ENDPOINT:http://127.0.0.1:30110} customLabel: public diff --git a/demo/demo-cse-v2/test-client/src/main/resources/log4j2.xml b/demo/demo-cse-v2/test-client/src/main/resources/log4j2.xml index 56fc9a0a676..9ac74305e0c 100644 --- a/demo/demo-cse-v2/test-client/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v2/test-client/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-edge/README.md b/demo/demo-edge/README.md new file mode 100644 index 00000000000..b5a552e0668 --- /dev/null +++ b/demo/demo-edge/README.md @@ -0,0 +1,17 @@ +# About edge service API compatibility + +* Edge service use the latest version of the microservice meta. e.g. For business 1.0.0, 1.1.0, 2.0.0 have the following APIs: + + * 1.0.0: /business/v1/add + * 1.1.0: /business/v1/add, /business/v1/dec + * 2.0.0: /business/v2/add, /business/v2/dec + + If users invoke /business/v1/add, edge service will give NOT FOUND, because 2.0.0 microservice meta do not have this API. Even using router to route all /business/v1/* requests to 1.1.0, path locating happens before load balance. + +* It's very important to keep your API compatibility cross versions if these versions need work together. e.g. + + * 1.0.0: /business/v1/add + * 1.1.0: /business/v1/add, /business/v1/dec + * 2.0.0: /business/v1/add, /business/v1/dec, /business/v2/add, /business/v2/dec + + Together with router, /business/v1/add will go correctly to 1.0.0 or 1.1.0, and /business/v2/add will go correctly to 2.0.0. Without router, /business/v2/add may route to 1.0.0 or 1.1.0 and NOT FOUND is reported. diff --git a/demo/demo-edge/authentication/pom.xml b/demo/demo-edge/authentication/pom.xml index fb7049292a2..4849faf28fe 100644 --- a/demo/demo-edge/authentication/pom.xml +++ b/demo/demo-edge/authentication/pom.xml @@ -21,19 +21,11 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT authentication Java Chassis::Demo::Edge::Authentication - - org.apache.servicecomb - provider-springmvc - - - org.apache.servicecomb - transport-rest-vertx - org.apache.servicecomb.demo.edge.authentication.AuthMain @@ -51,28 +43,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/AuthMain.java b/demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/AuthMain.java index 6d8b5e946fe..4928706edcd 100644 --- a/demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/AuthMain.java +++ b/demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/AuthMain.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.demo.edge.authentication; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class AuthMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(AuthMain.class).web(WebApplicationType.NONE).run(args); diff --git a/demo/demo-edge/authentication/src/main/resources/microservice.yaml b/demo/demo-edge/authentication/src/main/resources/microservice.yaml index aed5cc96fb2..038b64eb1ec 100644 --- a/demo/demo-edge/authentication/src/main/resources/microservice.yaml +++ b/demo/demo-edge/authentication/src/main/resources/microservice.yaml @@ -15,13 +15,13 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: edge -service_description: - name: auth - version: 0.0.1 servicecomb: service: - registry: + application: edge + name: auth + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:7070?protocol=http2 diff --git a/demo/demo-edge/business-1-1-0/pom.xml b/demo/demo-edge/business-1-1-0/pom.xml index 8f71fb843b9..1cef9b01cab 100644 --- a/demo/demo-edge/business-1-1-0/pom.xml +++ b/demo/demo-edge/business-1-1-0/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT business-1-1-0 Java Chassis::Demo::Edge::Business 1.1.0 @@ -30,18 +30,6 @@ - - org.apache.servicecomb - provider-springmvc - - - org.apache.servicecomb - transport-rest-vertx - - - org.apache.servicecomb - transport-rest-servlet - org.apache.servicecomb.demo model @@ -61,28 +49,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-edge/business-1-1-0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V1_1_0.java b/demo/demo-edge/business-1-1-0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V1_1_0.java index c91eb98eb28..f725fa24e0e 100644 --- a/demo/demo-edge/business-1-1-0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V1_1_0.java +++ b/demo/demo-edge/business-1-1-0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V1_1_0.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.demo.edge.business; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class BusinessMain_V1_1_0 { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(BusinessMain_V1_1_0.class).web(WebApplicationType.NONE).run(args); diff --git a/demo/demo-edge/business-1-1-0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java b/demo/demo-edge/business-1-1-0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java index 0c5c18e0dee..38420d285d6 100644 --- a/demo/demo-edge/business-1-1-0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java +++ b/demo/demo-edge/business-1-1-0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java @@ -21,6 +21,8 @@ import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; import org.apache.servicecomb.demo.edge.model.ResultWithInstance; import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -28,6 +30,13 @@ @RestSchema(schemaId = "news-v1") @RequestMapping(path = "/business/v1") public class Impl { + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + @RequestMapping(path = "/channel/news/subscribe", method = RequestMethod.POST) public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase request) { AppClientDataRsp response = new AppClientDataRsp(); @@ -38,11 +47,11 @@ public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase requ @RequestMapping(path = "/add", method = RequestMethod.GET) public ResultWithInstance add(int x, int y) { - return ResultWithInstance.create(x + y); + return ResultWithInstance.create(x + y, environment); } @RequestMapping(path = "/dec", method = RequestMethod.GET) public ResultWithInstance dec(int x, int y) { - return ResultWithInstance.create(x - y); + return ResultWithInstance.create(x - y, environment); } } diff --git a/demo/demo-edge/business-1-1-0/src/main/resources/microservice.yaml b/demo/demo-edge/business-1-1-0/src/main/resources/microservice.yaml index 3ea33d77160..0fa3a1764e4 100644 --- a/demo/demo-edge/business-1-1-0/src/main/resources/microservice.yaml +++ b/demo/demo-edge/business-1-1-0/src/main/resources/microservice.yaml @@ -15,13 +15,13 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: edge -service_description: - name: business - version: 1.1.0 servicecomb: service: - registry: + application: edge + name: business + version: 1.1.0 + registry: + sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8090 diff --git a/demo/demo-edge/business-1.0.0/pom.xml b/demo/demo-edge/business-1.0.0/pom.xml index ac6c66e88a7..d7e35748fef 100644 --- a/demo/demo-edge/business-1.0.0/pom.xml +++ b/demo/demo-edge/business-1.0.0/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT business-1-0-0 Java Chassis::Demo::Edge::Business 1.0.0 @@ -32,18 +32,6 @@ - - org.apache.servicecomb - provider-springmvc - - - org.apache.servicecomb - transport-rest-vertx - - - org.apache.servicecomb - transport-rest-servlet - org.apache.servicecomb.demo model @@ -63,28 +51,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-edge/business-1.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V1_0_0.java b/demo/demo-edge/business-1.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V1_0_0.java index 62392557fbd..e1a57c7e8b1 100644 --- a/demo/demo-edge/business-1.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V1_0_0.java +++ b/demo/demo-edge/business-1.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V1_0_0.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.demo.edge.business; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class BusinessMain_V1_0_0 { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(BusinessMain_V1_0_0.class).web(WebApplicationType.NONE).run(args); diff --git a/demo/demo-edge/business-1.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java b/demo/demo-edge/business-1.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java index 9ce38698699..1e0d1b84bb8 100644 --- a/demo/demo-edge/business-1.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java +++ b/demo/demo-edge/business-1.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java @@ -21,6 +21,8 @@ import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; import org.apache.servicecomb.demo.edge.model.ResultWithInstance; import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -28,6 +30,13 @@ @RestSchema(schemaId = "news-v1") @RequestMapping(path = "/business/v1") public class Impl { + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + @RequestMapping(path = "/channel/news/subscribe", method = RequestMethod.POST) public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase request) { AppClientDataRsp response = new AppClientDataRsp(); @@ -38,6 +47,6 @@ public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase requ @RequestMapping(path = "/add", method = RequestMethod.GET) public ResultWithInstance add(int x, int y) { - return ResultWithInstance.create(x + y); + return ResultWithInstance.create(x + y, environment); } } diff --git a/demo/demo-edge/business-1.0.0/src/main/resources/microservice.yaml b/demo/demo-edge/business-1.0.0/src/main/resources/microservice.yaml index d85b4b5cd8d..cc3c45ed92c 100644 --- a/demo/demo-edge/business-1.0.0/src/main/resources/microservice.yaml +++ b/demo/demo-edge/business-1.0.0/src/main/resources/microservice.yaml @@ -15,13 +15,13 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: edge -service_description: - name: business - version: 1.0.0 servicecomb: service: - registry: + application: edge + name: business + version: 1.0.0 + registry: + sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 diff --git a/demo/demo-edge/business-2.0.0/pom.xml b/demo/demo-edge/business-2.0.0/pom.xml index b4344e86ddc..3500be47d75 100644 --- a/demo/demo-edge/business-2.0.0/pom.xml +++ b/demo/demo-edge/business-2.0.0/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT business-2-0-0 Java Chassis::Demo::Edge::Business 2.0.0 @@ -32,18 +32,6 @@ - - org.apache.servicecomb - provider-springmvc - - - org.apache.servicecomb - transport-rest-vertx - - - org.apache.servicecomb - transport-rest-servlet - org.apache.servicecomb.demo model @@ -63,28 +51,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V2_0_0.java b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V2_0_0.java index 9090793c8aa..81559ce431f 100644 --- a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V2_0_0.java +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V2_0_0.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.demo.edge.business; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class BusinessMain_V2_0_0 { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(BusinessMain_V2_0_0.class).web(WebApplicationType.NONE).run(args); diff --git a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/EdgeServiceGovernanceService.java b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/EdgeServiceGovernanceService.java index f694cac5001..f8d951cd226 100644 --- a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/EdgeServiceGovernanceService.java +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/EdgeServiceGovernanceService.java @@ -29,8 +29,10 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; @RestSchema(schemaId = "EdgeServiceGovernanceService") @RequestMapping(path = "/business/v2") @@ -43,8 +45,8 @@ public class EdgeServiceGovernanceService { @GetMapping("/testEdgeServiceRetry") @ApiResponses({ - @ApiResponse(code = 200, response = String.class, message = ""), - @ApiResponse(code = 502, response = String.class, message = "")}) + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)), description = ""), + @ApiResponse(responseCode = "502", content = @Content(schema = @Schema(implementation = String.class)), description = "")}) public String testEdgeServiceRetry(@RequestParam(name = "invocationID") String invocationID) { LOGGER.info("invoke service: {}", invocationID); retryTimes.putIfAbsent(invocationID, 0); @@ -60,8 +62,8 @@ public String testEdgeServiceRetry(@RequestParam(name = "invocationID") String i @GetMapping("/testEdgeServiceInstanceIsolation") @ApiResponses({ - @ApiResponse(code = 200, response = String.class, message = ""), - @ApiResponse(code = 502, response = String.class, message = "")}) + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)), description = ""), + @ApiResponse(responseCode = "502", content = @Content(schema = @Schema(implementation = String.class)), description = "")}) public String testEdgeServiceInstanceIsolation(@RequestParam(name = "name") String name) { if (instanceIsolationIndex.getAndIncrement() % 3 != 0) { throw new InvocationException(502, "InstanceIsolation", "InstanceIsolation"); diff --git a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java index 046259713b7..fde945e50bf 100644 --- a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java @@ -17,40 +17,24 @@ package org.apache.servicecomb.demo.edge.business; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.RandomAccessFile; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.io.FileUtils; import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; -import org.apache.servicecomb.demo.edge.model.DependTypeA; -import org.apache.servicecomb.demo.edge.model.RecursiveSelfType; import org.apache.servicecomb.demo.edge.model.ResultWithInstance; -import org.apache.servicecomb.demo.edge.model.User; import org.apache.servicecomb.provider.rest.common.RestSchema; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; - -@RestSchema(schemaId = "news-v2") -@RequestMapping(path = "/business/v2") +@RestSchema(schemaId = "news-v1") +@RequestMapping(path = "/business/v1") public class Impl { - File tempDir = new File("target/downloadTemp"); + private Environment environment; - public Impl() throws IOException { - FileUtils.forceMkdir(tempDir); + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; } @RequestMapping(path = "/channel/news/subscribe", method = RequestMethod.POST) @@ -63,52 +47,11 @@ public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase requ @RequestMapping(path = "/add", method = RequestMethod.GET) public ResultWithInstance add(int x, int y) { - return ResultWithInstance.create(x + y); + return ResultWithInstance.create(x + y, environment); } @RequestMapping(path = "/dec", method = RequestMethod.GET) public ResultWithInstance dec(int x, int y) { - return ResultWithInstance.create(x - y); - } - - @GetMapping(path = "/download") - @ApiResponses({ - @ApiResponse(code = 200, response = File.class, message = ""), - }) - public ResponseEntity download() throws IOException { - return ResponseEntity - .ok() - .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=download.txt") - .body(new ByteArrayInputStream("download".getBytes(StandardCharsets.UTF_8))); - } - - protected File createBigFile() throws IOException { - File file = new File(tempDir, "bigFile.txt"); - file.delete(); - RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); - randomAccessFile.setLength(10 * 1024 * 1024); - randomAccessFile.close(); - return file; - } - - @GetMapping(path = "/bigFile") - public File bigFile() throws IOException { - return createBigFile(); - } - - @PostMapping(path = "recursiveSelf") - public RecursiveSelfType recursiveSelf(@RequestBody RecursiveSelfType value) { - return value; - } - - @PostMapping(path = "dependType") - public DependTypeA dependType(@RequestBody DependTypeA value) { - return value; - } - - @PostMapping(path = "encrypt") - public User encrypt(@RequestBody User value) { - return value; + return ResultWithInstance.create(x - y, environment); } } diff --git a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/ImplV2.java b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/ImplV2.java new file mode 100644 index 00000000000..0e5eabf290f --- /dev/null +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/ImplV2.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.edge.business; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.FileUtils; +import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; +import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; +import org.apache.servicecomb.demo.edge.model.DependTypeA; +import org.apache.servicecomb.demo.edge.model.RecursiveSelfType; +import org.apache.servicecomb.demo.edge.model.ResultWithInstance; +import org.apache.servicecomb.demo.edge.model.User; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; + +@RestSchema(schemaId = "news-v2") +@RequestMapping(path = "/business/v2") +public class ImplV2 { + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + File tempDir = new File("target/downloadTemp"); + + public ImplV2() throws IOException { + FileUtils.forceMkdir(tempDir); + } + + @RequestMapping(path = "/channel/news/subscribe", method = RequestMethod.POST) + public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase request) { + AppClientDataRsp response = new AppClientDataRsp(); + String rsp = "result from 2.0.0"; + response.setRsp(rsp); + return response; + } + + @RequestMapping(path = "/add", method = RequestMethod.GET) + public ResultWithInstance add(int x, int y) { + return ResultWithInstance.create(x + y, environment); + } + + @RequestMapping(path = "/dec", method = RequestMethod.GET) + public ResultWithInstance dec(int x, int y) { + return ResultWithInstance.create(x - y, environment); + } + + @GetMapping(path = "/download") + @ApiResponses({ + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = File.class)), description = ""), + }) + public ResponseEntity download() throws IOException { + return ResponseEntity + .ok() + .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=download.txt") + .body(new ByteArrayInputStream("download".getBytes(StandardCharsets.UTF_8))); + } + + protected File createBigFile() throws IOException { + File file = new File(tempDir, "bigFile.txt"); + file.delete(); + RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); + randomAccessFile.setLength(10 * 1024 * 1024); + randomAccessFile.close(); + return file; + } + + @GetMapping(path = "/bigFile") + public File bigFile() throws IOException { + return createBigFile(); + } + + @PostMapping(path = "recursiveSelf") + public RecursiveSelfType recursiveSelf(@RequestBody RecursiveSelfType value) { + return value; + } + + @PostMapping(path = "dependType") + public DependTypeA dependType(@RequestBody DependTypeA value) { + return value; + } + + @PostMapping(path = "encrypt") + public User encrypt(@RequestBody User value) { + return value; + } +} diff --git a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/CustomExceptionToProducerResponseConverter.java b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/CustomExceptionToProducerResponseConverter.java index c5f8d9025fb..9fd5aeec777 100644 --- a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/CustomExceptionToProducerResponseConverter.java +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/CustomExceptionToProducerResponseConverter.java @@ -17,14 +17,13 @@ package org.apache.servicecomb.demo.edge.business.error; -import javax.annotation.Nullable; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.Response.StatusType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.StatusType; + public class CustomExceptionToProducerResponseConverter implements ExceptionConverter { @Override @@ -38,7 +37,7 @@ public int getOrder() { } @Override - public InvocationException convert(@Nullable Invocation invocation, IllegalStateException e, + public InvocationException convert(Invocation invocation, IllegalStateException e, StatusType genericStatus) { IllegalStateErrorData data = new IllegalStateErrorData(); data.setId(500); diff --git a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/ErrorService.java b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/ErrorService.java index 8bdba9c2bc3..f4b56e58782 100644 --- a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/ErrorService.java +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/ErrorService.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.demo.edge.business.error; -import javax.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; diff --git a/demo/demo-edge/business-2.0.0/src/main/resources/microservice.yaml b/demo/demo-edge/business-2.0.0/src/main/resources/microservice.yaml index 2866854302f..45a9f2ff743 100644 --- a/demo/demo-edge/business-2.0.0/src/main/resources/microservice.yaml +++ b/demo/demo-edge/business-2.0.0/src/main/resources/microservice.yaml @@ -15,13 +15,13 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: edge -service_description: - name: business - version: 2.0.0 servicecomb: service: - registry: + application: edge + name: business + version: 2.0.0 + registry: + sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8091 diff --git a/demo/demo-edge/consumer/pom.xml b/demo/demo-edge/consumer/pom.xml index 11b08d743a1..c26ca048528 100644 --- a/demo/demo-edge/consumer/pom.xml +++ b/demo/demo-edge/consumer/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT consumer Java Chassis::Demo::Edge::Consumer @@ -32,22 +32,6 @@ - - org.apache.servicecomb - provider-pojo - - - org.apache.servicecomb - provider-springmvc - - - org.apache.servicecomb - transport-rest-vertx - - - org.apache.servicecomb - transport-rest-servlet - org.apache.servicecomb.demo demo-schema @@ -63,24 +47,212 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + authentication:${project.version} + authentication + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/authentication-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 7070 + + + + + + 7070:7070 + + + service-center + + + + + business-1-0-0:${project.version} + business-1-0-0 + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/business-1-0-0-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 8080:8080 + + + service-center + + + + + business-1-1-0:${project.version} + business-1-1-0 + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/business-1-1-0-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8090 + + + + + + 8090:8090 + + + service-center + + + + + business-2-0-0:${project.version} + business-2-0-0 + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/business-2-0-0-${project.version}.jar + + + service-center:sc.servicecomb.io + + + Register microservice instance success + + + 8091 + + + + + + 8091:8091 + + + service-center + + + + + edge-service:${project.version} + edge-service + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/edge-service-${project.version}.jar + + + service-center:sc.servicecomb.io + + + Register microservice instance success + + + + 18090 + + + + + + 18090:18090 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config-edge - ${project.version} - - - - diff --git a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java index 52759e4b122..ab10a7c3713 100644 --- a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java +++ b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java @@ -26,6 +26,7 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; import org.apache.servicecomb.demo.edge.model.DependTypeA; @@ -33,12 +34,11 @@ import org.apache.servicecomb.demo.edge.model.RecursiveSelfType; import org.apache.servicecomb.demo.edge.model.ResultWithInstance; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.springframework.core.env.Environment; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -49,10 +49,10 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; public class Consumer { - RestTemplate template = RestTemplateBuilder.create(); + RestOperations template = RestTemplateBuilder.create(); ChannelRequestBase request = new ChannelRequestBase(); @@ -66,6 +66,10 @@ public class Consumer { List decV2Result = new ArrayList<>(); + DiscoveryManager discoveryManager = BeanUtils.getBean(DiscoveryManager.class); + + Environment environment = BeanUtils.getBean(Environment.class); + public Consumer() { request.setDeviceId("2a5cc42ff60006ac"); request.setServiceToken( @@ -172,7 +176,7 @@ protected void testErrorCode() { try { Map raw = template.getForObject(url + "?x=99&y=3", Map.class); } catch (HttpServerErrorException e) { - Assert.isTrue(e.getRawStatusCode() == 500, "x99"); + Assert.isTrue(e.getStatusCode().value() == 500, "x99"); Assert.isTrue(e.getResponseBodyAsString().contains("un expected NPE test."), "x99"); } @@ -180,14 +184,14 @@ protected void testErrorCode() { template.getForObject(url + "?x=88&y=3", Map.class); Assert.isTrue(false, "x88"); } catch (HttpClientErrorException e) { - Assert.isTrue(e.getRawStatusCode() == 403, "x88"); + Assert.isTrue(e.getStatusCode().value() == 403, "x88"); Assert.isTrue(e.getResponseBodyAsString().equals("{\"id\":12,\"message\":\"not allowed id.\"}"), "x88"); } try { template.getForObject(url + "?x=77&y=3", Map.class); Assert.isTrue(false, "x77"); } catch (HttpServerErrorException e) { - Assert.isTrue(e.getRawStatusCode() == 500, "x77"); + Assert.isTrue(e.getStatusCode().value() == 500, "x77"); Assert.isTrue(e.getResponseBodyAsString().equals("{\"id\":500,\"message\":\"77\",\"state\":\"77\"}"), "x77"); } } @@ -268,23 +272,19 @@ protected void invoke(String appendUrl, int x, int y, List r } private URIEndpointObject prepareEdge(String prefix) { - Microservice microservice = RegistrationManager.INSTANCE.getMicroservice(); - MicroserviceInstance microserviceInstance = (MicroserviceInstance) DiscoveryManager.INSTANCE - .getAppManager() - .getOrCreateMicroserviceVersionRule(microservice.getAppId(), "edge", DefinitionConst.VERSION_RULE_ALL) - .getVersionedCache() - .mapData() - .values() + DiscoveryInstance instance = discoveryManager.findServiceInstances( + BootStrapProperties.readApplication(environment), "edge") .stream() .findFirst() .get(); - URIEndpointObject edgeAddress = new URIEndpointObject(microserviceInstance.getEndpoints().get(0)); + URIEndpointObject edgeAddress = new URIEndpointObject(instance.getEndpoints().get(0)); edgePrefix = String.format("http://%s:%d/%s/business", edgeAddress.getHostOrIp(), edgeAddress.getPort(), prefix); return edgeAddress; } protected void invokeBusiness(String urlPrefix, ChannelRequestBase request) { - for (int i = 0; i < 3; i++) { + List result = new ArrayList<>(6); + for (int i = 0; i < 6; i++) { String url = urlPrefix + "/channel/news/subscribe"; HttpHeaders headers = new HttpHeaders(); @@ -293,7 +293,10 @@ protected void invokeBusiness(String urlPrefix, ChannelRequestBase request) { HttpEntity entity = new HttpEntity<>(request, headers); ResponseEntity response = template.postForEntity(url, entity, AppClientDataRsp.class); - Assert.isTrue(response.getBody().getRsp().equals("result from 1.1.0"), response.getBody().getRsp()); + result.add(response.getBody().getRsp()); } + Assert.isTrue(result.contains("result from 2.0.0"), "invokeBusiness not balance"); + Assert.isTrue(result.contains("result from 1.1.0"), "invokeBusiness not balance"); + Assert.isTrue(result.contains("result from 1.0.0"), "invokeBusiness not balance"); } } diff --git a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java index 7e6746b650e..ae462eb6fd1 100644 --- a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java +++ b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java @@ -20,13 +20,11 @@ import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class ConsumerMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(ConsumerMain.class).web(WebApplicationType.NONE).run(args); @@ -44,8 +42,9 @@ public static void runTest() throws Exception { new Consumer().run("rest"); System.out.println("Running url dispatcher."); new Consumer().run("url"); - System.out.println("Running http dispatcher."); - new Consumer().run("http"); +// Common Http Dispatcher do not have OperationMeta, can not use router. +// System.out.println("Running http dispatcher."); +// new Consumer().run("http"); System.out.println("All test case finished."); diff --git a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.java b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.java index ed9b6d156c2..ad774433957 100644 --- a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.java +++ b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.java @@ -21,33 +21,40 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class EdgeServiceGovernanceTest implements CategorizedTestCase { - RestTemplate template = RestTemplateBuilder.create(); + RestOperations template = RestTemplateBuilder.create(); String edgePrefix; + @Autowired + Environment environment; + + @Autowired + DiscoveryManager discoveryManager; + @Override public void testRestTransport() throws Exception { prepareEdge("url"); // edge service do not support retry // testEdgeServiceRetry(); - - testEdgeServiceInstanceIsolation(); testEdgeServiceInstanceBulkhead(); + testEdgeServiceInstanceIsolation(); // may isolate instance for 5 seconds. + Thread.sleep(6000); // ensure isolation is open for new requests } private void testEdgeServiceInstanceBulkhead() throws Exception { @@ -64,7 +71,7 @@ private void testEdgeServiceInstanceBulkhead() throws Exception { public void run() { try { String result = template.getForObject(url + "?name={1}", String.class, "hello"); - if (!"\"hello\"".equals(result)) { + if (!"hello".equals(result)) { notExpectedFailed.set(true); } } catch (Exception e) { @@ -92,6 +99,7 @@ private void testEdgeServiceInstanceIsolation() throws Exception { String url = edgePrefix + "/business/v2/testEdgeServiceInstanceIsolation"; CountDownLatch latch = new CountDownLatch(100); + AtomicBoolean expectedFailed404 = new AtomicBoolean(false); AtomicBoolean expectedFailed502 = new AtomicBoolean(false); AtomicBoolean expectedFailed503 = new AtomicBoolean(false); AtomicBoolean notExpectedFailed = new AtomicBoolean(false); @@ -103,19 +111,28 @@ private void testEdgeServiceInstanceIsolation() throws Exception { public void run() { try { String result = template.getForObject(url + "?name={1}", String.class, "hello"); - if (!"\"hello\"".equals(result)) { + if (!"hello".equals(result)) { notExpectedFailed.set(true); } } catch (Exception e) { - if (!(e instanceof HttpServerErrorException)) { - notExpectedFailed.set(true); - } else { + if (e instanceof HttpClientErrorException) { + // isolate 2.0.0 and other instance will give 404 + if (((HttpClientErrorException) e).getStatusCode().value() == 404) { + expectedFailed404.set(true); + } else { + notExpectedFailed.set(true); + } + } else if (e instanceof HttpServerErrorException) { + // instance isolated and return 503 if (((HttpServerErrorException) e).getStatusCode().value() == 503) { expectedFailed503.set(true); } + // provider throw 502 exception to trigger instance isolation if (((HttpServerErrorException) e).getStatusCode().value() == 502) { expectedFailed502.set(true); } + } else { + notExpectedFailed.set(true); } } latch.countDown(); @@ -126,26 +143,15 @@ public void run() { } latch.await(20, TimeUnit.SECONDS); + TestMgr.check(true, expectedFailed404.get()); TestMgr.check(true, expectedFailed502.get()); TestMgr.check(true, expectedFailed503.get()); TestMgr.check(false, notExpectedFailed.get()); } -// private void testEdgeServiceRetry() { -// String url = edgePrefix + "/business/v2/testEdgeServiceRetry"; -// String invocationID = UUID.randomUUID().toString(); -// String result = template.getForObject(url + "?invocationID={1}", String.class, invocationID); -// TestMgr.check(result, "try times: 3"); -// } - private URIEndpointObject prepareEdge(String prefix) { - Microservice microservice = RegistrationManager.INSTANCE.getMicroservice(); - MicroserviceInstance microserviceInstance = (MicroserviceInstance) DiscoveryManager.INSTANCE - .getAppManager() - .getOrCreateMicroserviceVersionRule(microservice.getAppId(), "edge", DefinitionConst.VERSION_RULE_ALL) - .getVersionedCache() - .mapData() - .values() + DiscoveryInstance microserviceInstance = discoveryManager.findServiceInstances( + BootStrapProperties.readApplication(environment), "edge") .stream() .findFirst() .get(); diff --git a/demo/demo-edge/consumer/src/main/resources/microservice.yaml b/demo/demo-edge/consumer/src/main/resources/microservice.yaml index 660dfbf2849..953b1e567e4 100644 --- a/demo/demo-edge/consumer/src/main/resources/microservice.yaml +++ b/demo/demo-edge/consumer/src/main/resources/microservice.yaml @@ -15,13 +15,13 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: edge -service_description: - name: consumer - version: 0.0.1 servicecomb: service: - registry: + application: edge + name: consumer + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 rest: client.http2.useAlpnEnabled: false @@ -29,5 +29,3 @@ servicecomb: chain: Consumer: default: loadbalance - references: - version-rule: 1.1.0 \ No newline at end of file diff --git a/demo/demo-edge/edge-service/pom.xml b/demo/demo-edge/edge-service/pom.xml index 1134c94b9a7..31fdcff3874 100644 --- a/demo/demo-edge/edge-service/pom.xml +++ b/demo/demo-edge/edge-service/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT edge-service Java Chassis::Demo::Edge::Service @@ -35,18 +35,6 @@ org.apache.servicecomb edge-core - - org.apache.servicecomb - handler-loadbalance - - - org.apache.servicecomb - handler-governance - - - org.apache.servicecomb - provider-pojo - @@ -61,28 +49,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/EdgeMain.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/EdgeMain.java index 599fd2594c3..b1c17a93cab 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/EdgeMain.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/EdgeMain.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.demo.edge; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class EdgeMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(EdgeMain.class).web(WebApplicationType.NONE).run(args); diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/EdgeDispatcher.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/EdgeDispatcher.java index ba7882d2f63..b900a427409 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/EdgeDispatcher.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/EdgeDispatcher.java @@ -22,7 +22,6 @@ import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.edge.core.AbstractEdgeDispatcher; -import org.apache.servicecomb.edge.core.CompatiblePathVersionMapper; import org.apache.servicecomb.edge.core.EdgeInvocationCreator; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; @@ -33,8 +32,6 @@ import io.vertx.ext.web.RoutingContext; public class EdgeDispatcher extends AbstractEdgeDispatcher { - private CompatiblePathVersionMapper versionMapper = new CompatiblePathVersionMapper(); - @Override public int getOrder() { return 10000; @@ -51,17 +48,16 @@ public void init(Router router) { protected void onRequest(RoutingContext context) { Map pathParams = context.pathParams(); String microserviceName = pathParams.get("param0"); - String pathVersion = pathParams.get("param1"); String path = context.request().path().substring(4); - requestByFilter(context, microserviceName, versionMapper.getOrCreate(pathVersion).getVersionRule(), path); + requestByFilter(context, microserviceName, path); } - protected void requestByFilter(RoutingContext context, String microserviceName, String versionRule, String path) { + protected void requestByFilter(RoutingContext context, String microserviceName, String path) { HttpServletRequestEx requestEx = new VertxServerRequestToHttpServletRequest(context); HttpServletResponseEx responseEx = new VertxServerResponseToHttpServletResponse(context.response()); InvocationCreator creator = new EdgeInvocationCreator(context, requestEx, responseEx, - microserviceName, versionRule, path); + microserviceName, path); new RestProducerInvocationFlow(creator, requestEx, responseEx) .run(); } diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptEdgeDispatcher.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptEdgeDispatcher.java index d6ce51d2d7d..76ea1840402 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptEdgeDispatcher.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptEdgeDispatcher.java @@ -24,8 +24,8 @@ import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; +import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.edge.core.AbstractEdgeDispatcher; -import org.apache.servicecomb.edge.core.CompatiblePathVersionMapper; import org.apache.servicecomb.edge.core.EdgeInvocationCreator; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; @@ -42,8 +42,6 @@ public class EncryptEdgeDispatcher extends AbstractEdgeDispatcher { private static final Logger LOGGER = LoggerFactory.getLogger(EncryptEdgeDispatcher.class); - private CompatiblePathVersionMapper versionMapper = new CompatiblePathVersionMapper(); - private Encrypt encrypt = Invoker.createProxy("auth", "encrypt", Encrypt.class); private String prefix = "encryptApi"; @@ -55,7 +53,7 @@ public int getOrder() { @Override public void init(Router router) { - String regex = "/" + prefix + "/([^\\\\/]+)/([^\\\\/]+)/(.*)"; + String regex = "/" + prefix + "/([^\\\\/]+)/(.*)"; // cookies handler are enabled by default start from 3.8.3 router.routeWithRegex(regex).handler(createBodyHandler()); router.routeWithRegex(regex).failureHandler(this::onFailure).handler(this::onRequest); @@ -107,26 +105,25 @@ private CompletableFuture queryHcr(HttpServerRequest httpServerRequest) { private void routeToBackend(RoutingContext context, Hcr hcr, String userId) { Map pathParams = context.pathParams(); String microserviceName = pathParams.get("param0"); - String pathVersion = pathParams.get("param1"); String path = context.request().path().substring(prefix.length() + 1); - requestByFilter(context, microserviceName, versionMapper.getOrCreate(pathVersion).getVersionRule(), path, + requestByFilter(context, microserviceName, path, new EncryptContext(hcr, userId)); } - protected void requestByFilter(RoutingContext context, String microserviceName, String versionRule, String path + protected void requestByFilter(RoutingContext context, String microserviceName, String path , EncryptContext encryptContext) { HttpServletRequestEx requestEx = new VertxServerRequestToHttpServletRequest(context); HttpServletResponseEx responseEx = new VertxServerResponseToHttpServletResponse(context.response()); InvocationCreator creator = new EdgeInvocationCreator(context, requestEx, responseEx, - microserviceName, versionRule, path) { + microserviceName, path) { @Override public CompletableFuture createAsync() { CompletableFuture result = super.createAsync(); return result.whenComplete((invocation, throwable) -> { if (throwable == null) { - invocation.getHandlerContext().put("encryptContext", encryptContext); + invocation.getHandlerContext().put(EdgeConst.ENCRYPT_CONTEXT, encryptContext); } }); } diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/DecodeBodyFilter.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/DecodeBodyFilter.java index c59dbda93ef..d9e0f332318 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/DecodeBodyFilter.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/DecodeBodyFilter.java @@ -19,17 +19,15 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.demo.edge.service.encrypt.EncryptContext; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.stereotype.Component; @@ -37,15 +35,14 @@ import com.fasterxml.jackson.databind.type.TypeFactory; @Component -public class DecodeBodyFilter implements ConsumerFilter { +public class DecodeBodyFilter extends AbstractFilter implements EdgeFilter { private JavaType bodyType = TypeFactory.defaultInstance().constructMapType(Map.class, String.class, String[].class); @Override - public int getOrder(InvocationType invocationType, String microservice) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1790; } - @Nonnull @Override public String getName() { return "test-edge-decode-body"; diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureRequestFilter.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureRequestFilter.java index a01119c4a93..42443b555b4 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureRequestFilter.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureRequestFilter.java @@ -22,16 +22,14 @@ import java.util.List; import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.demo.edge.service.encrypt.EncryptContext; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,15 +39,14 @@ import com.google.common.hash.Hashing; @Component -public class EdgeSignatureRequestFilter implements ConsumerFilter { +public class EdgeSignatureRequestFilter extends AbstractFilter implements EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(EdgeSignatureRequestFilter.class); @Override - public int getOrder(InvocationType invocationType, String microservice) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1800; } - @Nonnull @Override public String getName() { return "test-edge-signature-request"; diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureResponseFilter.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureResponseFilter.java index cea1f2643f2..a3909d75540 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureResponseFilter.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureResponseFilter.java @@ -19,16 +19,14 @@ import java.nio.charset.StandardCharsets; import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.demo.edge.service.encrypt.EncryptContext; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,18 +38,17 @@ import io.vertx.core.buffer.Buffer; @Component -public class EdgeSignatureResponseFilter implements ConsumerFilter { +public class EdgeSignatureResponseFilter extends AbstractFilter implements EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(EdgeSignatureResponseFilter.class); @Override - public int getOrder(InvocationType invocationType, String microservice) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1991; } - @Nonnull @Override public String getName() { - return "test-edge-signature-request"; + return "test-edge-signature-response"; } @Override diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/UserIdFilter.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/UserIdFilter.java index e7e7965e47e..fd20c0e6d7e 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/UserIdFilter.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/UserIdFilter.java @@ -18,26 +18,23 @@ import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.demo.edge.service.encrypt.EncryptContext; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.stereotype.Component; @Component -public class UserIdFilter implements ConsumerFilter { +public class UserIdFilter extends AbstractFilter implements EdgeFilter { @Override - public int getOrder(InvocationType invocationType, String microservice) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1790; } - @Nonnull @Override public String getName() { return "test-edge-user-id"; diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/AuthHandler.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/AuthHandler.java index 25f2aec5de8..4aeb44be08a 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/AuthHandler.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/AuthHandler.java @@ -19,24 +19,23 @@ import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; -import javax.ws.rs.core.Response.Status; - import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.provider.pojo.Invoker; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; +import jakarta.ws.rs.core.Response.Status; + @Component -public class AuthHandler implements ConsumerFilter { +public class AuthHandler extends AbstractFilter implements EdgeFilter { private static Logger LOGGER = LoggerFactory.getLogger(AuthHandler.class); private static Auth auth; @@ -46,19 +45,16 @@ public class AuthHandler implements ConsumerFilter { } @Override - public int getOrder(InvocationType invocationType, String microservice) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1980; } @Override - public boolean isEnabledForMicroservice(String microservice) { - if ("auth".equals(microservice)) { - return false; - } - return true; + public boolean enabledForMicroservice(String application, String serviceName) { + // Do not enable for service auth and not enable for internal management access(myself) + return !"auth".equals(serviceName) && !"edge".equals(serviceName); } - @Nonnull @Override public String getName() { return "test-auth"; diff --git a/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter b/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter deleted file mode 100644 index eb91526f3d5..00000000000 --- a/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter +++ /dev/null @@ -1,21 +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. -# - -org.apache.servicecomb.demo.edge.service.encrypt.filter.EdgeSignatureRequestFilter -org.apache.servicecomb.demo.edge.service.encrypt.filter.DecodeBodyFilter -org.apache.servicecomb.demo.edge.service.encrypt.filter.UserIdFilter -org.apache.servicecomb.demo.edge.service.encrypt.filter.EdgeSignatureResponseFilter \ No newline at end of file diff --git a/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.ResponseMetaMapper b/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.ResponseMetaMapper index d8a88a70544..dcc25e0a838 100644 --- a/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.ResponseMetaMapper +++ b/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.ResponseMetaMapper @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.demo.edge.service.CustomResponseMetaMapper \ No newline at end of file +org.apache.servicecomb.demo.edge.service.CustomResponseMetaMapper diff --git a/demo/demo-edge/edge-service/src/main/resources/microservice.yaml b/demo/demo-edge/edge-service/src/main/resources/microservice.yaml index 4b2e12b4a7c..56403677e38 100644 --- a/demo/demo-edge/edge-service/src/main/resources/microservice.yaml +++ b/demo/demo-edge/edge-service/src/main/resources/microservice.yaml @@ -15,13 +15,13 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: edge -service_description: - name: edge - version: 0.0.1 servicecomb: service: - registry: + application: edge + name: edge + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 edge: filter: @@ -57,12 +57,10 @@ servicecomb: prefixSegmentCount: 1 path: "/url/business/v1/.*" microserviceName: business - versionRule: 1.0.0-2.0.0 businessV2: prefixSegmentCount: 1 path: "/url/business/v2/.*" microserviceName: business - versionRule: 2.0.0-3.0.0 http: enabled: true mappings: @@ -70,19 +68,16 @@ servicecomb: prefixSegmentCount: 1 path: "/http/business/v2/.*" microserviceName: business - versionRule: 2.0.0 businessV1: prefixSegmentCount: 1 path: "/http/business/v1/add.*" microserviceName: business - versionRule: 1.0.0-1.2.0 businessV1_1: prefixSegmentCount: 1 path: "/http/business/v1/dec.*" microserviceName: business - versionRule: 1.1.0 -# 服务治理配置 + # 服务治理配置 matchGroup: testEdgeServiceRetry: | matches: @@ -96,11 +91,11 @@ servicecomb: matches: - apiPath: exact: "/business/v2/testEdgeServiceInstanceBulkhead" -# retry not supported now -# retry: -# testEdgeServiceRetry: | -# maxAttempts: 2 -# retryOnSame: 0 + # retry not supported now + # retry: + # testEdgeServiceRetry: | + # maxAttempts: 2 + # retryOnSame: 0 instanceIsolation: testEdgeServiceInstanceIsolation: | minimumNumberOfCalls: 10 @@ -109,9 +104,34 @@ servicecomb: failureRateThreshold: 50 slowCallRateThreshold: 100 slowCallDurationThreshold: 3000 - waitDurationInOpenState: 10000 + waitDurationInOpenState: 3000 permittedNumberOfCallsInHalfOpenState: 10 instanceBulkhead: testEdgeServiceInstanceBulkhead: | maxConcurrentCalls: 1 maxWaitDuration: 1 + + # enable router for edge service + router: + type: router + routeRule: + business: | + - precedence: 2 + match: + apiPath: + prefix: "/business/v2" + route: + - weight: 100 + tags: + version: 2.0.0 + - precedence: 1 + match: + apiPath: + prefix: "/business/v1/dec" + route: + - weight: 50 + tags: + version: 1.1.0 + - weight: 50 + tags: + version: 2.0.0 diff --git a/demo/demo-edge/model/pom.xml b/demo/demo-edge/model/pom.xml index 55e3a19e0e0..3adda2c13e4 100644 --- a/demo/demo-edge/model/pom.xml +++ b/demo/demo-edge/model/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT model Java Chassis::Demo::Edge::Model diff --git a/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/ResultWithInstance.java b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/ResultWithInstance.java index b9c3d3d83a1..c225f7fee34 100644 --- a/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/ResultWithInstance.java +++ b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/ResultWithInstance.java @@ -17,7 +17,8 @@ package org.apache.servicecomb.demo.edge.model; -import org.apache.servicecomb.registry.RegistrationManager; +import org.apache.servicecomb.config.BootStrapProperties; +import org.springframework.core.env.Environment; public class ResultWithInstance { private int result; @@ -28,12 +29,10 @@ public class ResultWithInstance { private String version; - public static ResultWithInstance create(int value) { + public static ResultWithInstance create(int value, Environment environment) { ResultWithInstance result = new ResultWithInstance(); result.setResult(value); - result.setInstanceId(RegistrationManager.INSTANCE.getMicroserviceInstance().getInstanceId()); - result.setServiceId(RegistrationManager.INSTANCE.getMicroservice().getServiceId()); - result.setVersion(RegistrationManager.INSTANCE.getMicroservice().getVersion()); + result.setVersion(BootStrapProperties.readServiceVersion(environment)); return result; } diff --git a/demo/demo-edge/pom.xml b/demo/demo-edge/pom.xml index d4aa39c124b..34cdcd1fccc 100644 --- a/demo/demo-edge/pom.xml +++ b/demo/demo-edge/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-edge Java Chassis::Demo::Edge @@ -31,11 +31,11 @@ org.apache.servicecomb - registry-service-center + solution-basic org.apache.servicecomb - registry-schema-discovery + registry-service-center org.apache.servicecomb diff --git a/demo/demo-etcd/README.md b/demo/demo-etcd/README.md new file mode 100644 index 00000000000..046bec6c968 --- /dev/null +++ b/demo/demo-etcd/README.md @@ -0,0 +1,5 @@ +# Notice + +This integration tests is designed for Etcd registry and configuration. And extra test cases include: + +* Test cases related to SpringMVC annotations that demo-springmvc can not cover. diff --git a/demo/demo-etcd/consumer/pom.xml b/demo/demo-etcd/consumer/pom.xml new file mode 100644 index 00000000000..20a03319d43 --- /dev/null +++ b/demo/demo-etcd/consumer/pom.xml @@ -0,0 +1,114 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-etcd + 3.4.0-SNAPSHOT + + + etcd-consumer + Java Chassis::Demo::Etcd::CONSUMER + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + com.google.protobuf + protobuf-java + 3.25.5 + runtime + + + org.apache.servicecomb + registry-etcd + + + org.apache.servicecomb + config-etcd + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java new file mode 100644 index 00000000000..f71738a9ed6 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.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.servicecomb.samples; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.ServerWebSocket; +import io.vertx.core.http.WebSocket; + +@RestSchema(schemaId = "ClientWebsocketController") +@RequestMapping(path = "/ws") +public class ClientWebsocketController { + interface ProviderService { + WebSocket websocket(); + } + + @RpcReference(schemaId = "WebsocketController", microserviceName = "provider") + private ProviderService providerService; + + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + public void websocket(ServerWebSocket serverWebsocket) { + WebSocket providerWebSocket = providerService.websocket(); + providerWebSocket.closeHandler(v -> serverWebsocket.close()); + providerWebSocket.textMessageHandler(m -> { + serverWebsocket.writeTextMessage(m); + }); + serverWebsocket.textMessageHandler(m -> { + providerWebSocket.writeTextMessage(m); + }); + } +} diff --git a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java new file mode 100644 index 00000000000..e66fb2cb3d0 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.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.servicecomb.samples; + +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@RestSchema(schemaId = "ConsumerController") +@RequestMapping(path = "/") +public class ConsumerController { + @RpcReference(schemaId = "ProviderController", microserviceName = "provider") + private ProviderService providerService; + + // consumer service which delegate the implementation to provider service. + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { + return providerService.sayHello(name); + } + + @GetMapping("/getConfig") + public String getConfig(@RequestParam("key") String key) { + return providerService.getConfig(key); + } + + @PostMapping(path = "/testContentType", consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public User testContentType(@RequestBody User user) { + return providerService.testContentType(user); + } +} diff --git a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java new file mode 100644 index 00000000000..f0e894d6745 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) +public class ConsumerHeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { + @RpcReference(microserviceName = "provider", schemaId = "HeaderParamWithListSchema") + private IHeaderParamWithListSchemaSpringMvc provider; + + @Override + public String headerListDefault(List headerList) { + return provider.headerListDefault(headerList); + } + + @Override + public String headerListCSV(List headerList) { + return provider.headerListCSV(headerList); + } + + @Override + public String headerListMULTI(List headerList) { + return provider.headerListMULTI(headerList); + } + + @Override + public String headerListSSV(List headerList) { + return provider.headerListSSV(headerList); + } + + @Override + public String headerListPIPES(List headerList) { + return provider.headerListPIPES(headerList); + } +} diff --git a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java new file mode 100644 index 00000000000..aa169801cf7 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +public class ConsumerReactiveStreamController { + interface ProviderReactiveStreamController { + Publisher sseString(); + + Publisher sseModel(); + } + + @RpcReference(microserviceName = "provider", schemaId = "ReactiveStreamController") + ProviderReactiveStreamController controller; + + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + @Transport(name = CoreConst.RESTFUL) + public Publisher sseString() { + return controller.sseString(); + } + + @GetMapping("/sseModel") + @Transport(name = CoreConst.RESTFUL) + public Publisher sseModel() { + return controller.sseModel(); + } +} diff --git a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java new file mode 100644 index 00000000000..2101e6512f8 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class EtcdConsumerApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(EtcdConsumerApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java new file mode 100644 index 00000000000..2899546c752 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.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.servicecomb.samples; + +public interface ProviderService { + String sayHello(String name); + + String getConfig(String key); + + User testContentType(User user); +} diff --git a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/User.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/User.java new file mode 100644 index 00000000000..ff421930ef2 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/User.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.servicecomb.samples; + + +public class User { + + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/demo/demo-etcd/consumer/src/main/resources/application.yml b/demo/demo-etcd/consumer/src/main/resources/application.yml new file mode 100644 index 00000000000..e0db5f1434e --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-etcd + version: 0.0.1 + name: consumer + properties: + group: red + registry: + etcd: + connectString: http://127.0.0.1:2379 + config: + etcd: + connectString: http://127.0.0.1:2379 + + rest: + address: 0.0.0.0:9092?websocketEnabled=true + server: + websocket-prefix: /ws diff --git a/demo/demo-etcd/consumer/src/main/resources/log4j2.xml b/demo/demo-etcd/consumer/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-etcd/gateway/pom.xml b/demo/demo-etcd/gateway/pom.xml new file mode 100644 index 00000000000..0b52381a492 --- /dev/null +++ b/demo/demo-etcd/gateway/pom.xml @@ -0,0 +1,113 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-etcd + 3.4.0-SNAPSHOT + + + etcd-gateway + Java Chassis::Demo::Etcd::GATEWAY + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + edge-core + + + com.google.protobuf + protobuf-java + 3.25.5 + runtime + + + org.apache.servicecomb + registry-etcd + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java b/demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java new file mode 100644 index 00000000000..7d58caafd9d --- /dev/null +++ b/demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class GatewayApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-etcd/gateway/src/main/resources/application.yml b/demo/demo-etcd/gateway/src/main/resources/application.yml new file mode 100644 index 00000000000..f526eb3de70 --- /dev/null +++ b/demo/demo-etcd/gateway/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-etcd + version: 0.0.1 + name: gateway + registry: + etcd: + enabled: true + connectString: http://127.0.0.1:2379 + + rest: + address: 0.0.0.0:9090?websocketEnabled=true + server: + websocket-prefix: /ws + + http: + dispatcher: + edge: + default: + enabled: false + url: + enabled: true + pattern: /(.*) + mappings: + consumer: + prefixSegmentCount: 0 + path: "/.*" + microserviceName: consumer + versionRule: 0.0.0+ + websocket: + mappings: + consumer: + prefixSegmentCount: 0 + path: "/ws/.*" + microserviceName: consumer + versionRule: 0.0.0+ diff --git a/demo/demo-etcd/gateway/src/main/resources/log4j2.xml b/demo/demo-etcd/gateway/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-etcd/gateway/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-etcd/pom.xml b/demo/demo-etcd/pom.xml new file mode 100644 index 00000000000..c615edd88b1 --- /dev/null +++ b/demo/demo-etcd/pom.xml @@ -0,0 +1,61 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-etcd + Java Chassis::Demo::Etcd + pom + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + solution-basic + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-api + + + + + provider + consumer + gateway + test-client + + + diff --git a/demo/demo-etcd/provider/pom.xml b/demo/demo-etcd/provider/pom.xml new file mode 100644 index 00000000000..8d8c005a1e1 --- /dev/null +++ b/demo/demo-etcd/provider/pom.xml @@ -0,0 +1,123 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-etcd + 3.4.0-SNAPSHOT + + + etcd-provider + Java Chassis::Demo::Etcd::PROVIDER + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + com.google.protobuf + protobuf-java + 3.25.5 + runtime + + + org.apache.servicecomb + registry-etcd + + + org.apache.servicecomb + config-etcd + + + + io.reactivex.rxjava3 + rxjava + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/EtcdProviderApplication.java b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/EtcdProviderApplication.java new file mode 100644 index 00000000000..388b962dc61 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/EtcdProviderApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class EtcdProviderApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(EtcdProviderApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java new file mode 100644 index 00000000000..8a773f91c72 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) +public class HeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { + @Override + public String headerListDefault(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListCSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListMULTI(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListSSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListPIPES(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } +} diff --git a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java new file mode 100644 index 00000000000..677c20ed106 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@RestSchema(schemaId = "ProviderController") +@RequestMapping(path = "/") +public class ProviderController implements InitializingBean { + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + // a very simple service to echo the request parameter + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { +// return "Hello " + environment.getProperty("servicecomb.rest.address"); + return "Hello " + name; + } + + @GetMapping("/getConfig") + public String getConfig(@RequestParam("key") String key) { + return environment.getProperty(key); + } + + @PostMapping(path = "/testContentType", consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public User testContentType(@RequestBody User user) { + return user; + } + + @Override + public void afterPropertiesSet() throws Exception { + } +} diff --git a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java new file mode 100644 index 00000000000..8108d15fd46 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.reactivex.rxjava3.core.Flowable; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +@Transport(name = CoreConst.RESTFUL) +public class ReactiveStreamController { + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + public Publisher sseString() { + return Flowable.fromArray("a", "b", "c"); + } + + @GetMapping("/sseModel") + public Publisher sseModel() { + return Flowable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS) + .map(item -> new Model("jack", item.intValue())); + } +} diff --git a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/User.java b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/User.java new file mode 100644 index 00000000000..ca12402d672 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/User.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + +public class User { + + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java new file mode 100644 index 00000000000..3bc88c64ac5 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.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.servicecomb.samples; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.ServerWebSocket; + +@RestSchema(schemaId = "WebsocketController") +@RequestMapping(path = "/ws") +public class WebsocketController { + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + public void websocket(ServerWebSocket serverWebsocket) { + // Client may have not registered message handler, and messages sent may get lost. + // So we sleep for a while to send message. + AtomicInteger receiveCount = new AtomicInteger(0); + serverWebsocket.textMessageHandler(s -> { + receiveCount.getAndIncrement(); + }); + serverWebsocket.closeHandler((v) -> System.out.println("closed")); + + new Thread(() -> { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + serverWebsocket.writeTextMessage("hello"); + + for (int i = 0; i < 5; i++) { + serverWebsocket.writeTextMessage("hello " + i); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + serverWebsocket.writeTextMessage("total " + receiveCount.get()); + serverWebsocket.close(); + }).start(); + } +} diff --git a/demo/demo-etcd/provider/src/main/resources/application.yml b/demo/demo-etcd/provider/src/main/resources/application.yml new file mode 100644 index 00000000000..da2051cd77e --- /dev/null +++ b/demo/demo-etcd/provider/src/main/resources/application.yml @@ -0,0 +1,55 @@ +# +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- +# spring boot configurations +servicecomb: + service: + application: demo-etcd + version: 0.0.1 + name: provider + properties: + group: green + registry: + etcd: + connectString: http://127.0.0.1:2379 + config: + etcd: + connectString: http://127.0.0.1:2379 + instance-tag: tag1 + + rest: + address: 0.0.0.0:9094?websocketEnabled=true + server: + websocket-prefix: /ws + + cors: + enabled: true + origin: "*" + allowCredentials: false + allowedMethod: "*" + maxAge: 3600 + + +key1: 1 +key2: 3 +key3: 5 + +test1: env +test2: applition +test3: service +test4: version +test5: tag diff --git a/demo/demo-etcd/provider/src/main/resources/log4j2.xml b/demo/demo-etcd/provider/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-etcd/test-client/pom.xml b/demo/demo-etcd/test-client/pom.xml new file mode 100644 index 00000000000..ab839d762f5 --- /dev/null +++ b/demo/demo-etcd/test-client/pom.xml @@ -0,0 +1,211 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-etcd + 3.4.0-SNAPSHOT + + + etcd-test-client + Java Chassis::Demo::Etcd::TEST-CLIENT + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + registry-local + + + io.etcd + jetcd-core + + + com.google.protobuf + protobuf-java + 3.25.5 + runtime + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + bitnami/etcd:3.5.16 + etcd + + alias + + etcdserver + + + 2379 + + + + + + 2379:2379 + + + yes + + + + + etcd-provider:${project.version} + etcd-provider + + alias + + + -Dservicecomb.registry.etcd.connectString=http://etcd:2379 + -Dservicecomb.config.etcd.connectString=http://etcd:2379 + + /maven/maven/etcd-provider-${project.version}.jar + + + etcd:etcd + + + ServiceComb is ready + + + 9094 + + + + + + 9094:9094 + + + + + etcd-consumer:${project.version} + etcd-consumer + + alias + + + -Dservicecomb.registry.etcd.connectString=http://etcd:2379 + -Dservicecomb.config.etcd.connectString=http://etcd:2379 + + /maven/maven/etcd-consumer-${project.version}.jar + + + etcd:etcd + + + ServiceComb is ready + + + 9092 + + + + + + 9092:9092 + + + + + etcd-gateway:${project.version} + etcd-gateway + + alias + + + -Dservicecomb.registry.etcd.connectString=http://etcd:2379 + + /maven/maven/etcd-gateway-${project.version}.jar + + + etcd:etcd + + + ServiceComb is ready + + + 9090 + + + + + + 9090:9090 + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/Config.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/Config.java new file mode 100644 index 00000000000..2e9105cdd4a --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/Config.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +public interface Config { + String GATEWAY_URL = "http://localhost:9090"; +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java new file mode 100644 index 00000000000..92432e01f5f --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.utils.ConditionWaiter.SleepUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +import io.etcd.jetcd.ByteSequence; +import io.etcd.jetcd.Client; + +@Component +public class EtcdConfigIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdConfigIT.class); + + @Override + public void testRestTransport() throws Exception { + + testEnvironment(); + testApplication(); + testService(); + testVersion(); + testTag(); + testOverride(); + } + + private void testOverride() { + + putValue("/servicecomb/config/environment/production/application2.properties", + "testValue=t1"); + putValue("/servicecomb/config/application/production/demo-etcd/application2.properties", + "testValue=t2"); + testGetConfig("testValue", "t2"); + putValue("/servicecomb/config/service/production/demo-etcd/provider/application2.properties", + "testValue=t3"); + testGetConfig("testValue", "t3"); + putValue("/servicecomb/config/version/production/demo-etcd/provider/0.0.1/application2.properties", + "testValue=t4"); + testGetConfig("testValue", "t4"); + putValue("/servicecomb/config/tag/production/demo-etcd/provider/0.0.1/tag1/application2.properties", + "testValue=t5"); + testGetConfig("testValue", "t5"); + } + + private void testEnvironment() { + + putValue("/servicecomb/config/environment/production/application.properties", + "test1=env"); + putValue("/servicecomb/config/environment/production/application.properties", + "test1=env1"); + + testGetConfig("test1", "env1"); + } + + + private void testApplication() { + + putValue("/servicecomb/config/application/production/demo-etcd/application.properties", + "test2=applition"); + putValue("/servicecomb/config/application/production/demo-etcd/application.properties", + "test2=applition2"); + testGetConfig("test2", "applition2"); + } + + private void testService() { + + putValue("/servicecomb/config/service/production/demo-etcd/provider/application.properties", + "test3=service"); + putValue("/servicecomb/config/service/production/demo-etcd/provider/application.properties", + "test3=service3"); + testGetConfig("test3", "service3"); + } + + private void testVersion() { + + putValue("/servicecomb/config/version/production/demo-etcd/provider/0.0.1/application.properties", + "test3=version"); + putValue("/servicecomb/config/version/production/demo-etcd/provider/0.0.1/application.properties", + "test4=version4"); + testGetConfig("test4", "version4"); + } + + private void testTag() { + + putValue("/servicecomb/config/tag/production/demo-etcd/provider/0.0.1/tag1/application.properties", + "test5=tag"); + putValue("/servicecomb/config/tag/production/demo-etcd/provider/0.0.1/tag1/application.properties", + "test5=tag5"); + testGetConfig("test5", "tag5"); + } + + + public void putValue(String key, String value) { + try (Client client = Client.builder().endpoints("http://localhost:2379").build()) { + + client.getKVClient().put( + ByteSequence.from(key, StandardCharsets.UTF_8), + ByteSequence.from(value, StandardCharsets.UTF_8) + ).get(); + + LOGGER.info("Value set successfully:{}", value); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void testGetConfig(String key, String expectValue) { + + String result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=" + key, String.class); + + for (int i = 0; i < 4; i++) { + if (StringUtils.equals(expectValue, result)) { + TestMgr.check(expectValue, result); + break; + } + SleepUtil.sleep(500, TimeUnit.MILLISECONDS); + } + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java new file mode 100644 index 00000000000..1b129acc2c6 --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HeaderParamWithListSchemaIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHeaderListDefault(); + testHeaderListMulti(); + testHeaderListCSV(); + testHeaderListSSV(); + testHeaderListPipes(); + } + + // default to multi + private void testHeaderListDefault() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListDefault", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListPipes() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a|b|c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListPIPES", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListSSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a b c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListSSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListCSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a,b,c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + + headers.add("headerList", "a, b, c"); + entity = new HttpEntity<>(headers); + result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListMulti() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListMULTI", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java new file mode 100644 index 00000000000..97e883fb45f --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.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.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HelloWorldIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHelloWorld(); + testGetConfig(); + } + + private void testGetConfig() { + String result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key1", String.class); + TestMgr.check("1", result); + result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key2", String.class); + TestMgr.check("3", result); + result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key3", String.class); + TestMgr.check("5", result); + } + + private void testHelloWorld() { + String result = template + .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); + TestMgr.check("Hello World", result); + + // test trace id added + MultiValueMap headers = new HttpHeaders(); + headers.add("X-B3-TraceId", "81de2eb7691c2bbb"); + HttpEntity entity = new HttpEntity(headers); + ResponseEntity response = + template.exchange(Config.GATEWAY_URL + "/sayHello?name=World", HttpMethod.GET, entity, String.class); + TestMgr.check(1, response.getHeaders().get("X-B3-TraceId").size()); + TestMgr.check("81de2eb7691c2bbb", response.getHeaders().getFirst("X-B3-TraceId")); + TestMgr.check("Hello World", response.getBody()); + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java new file mode 100644 index 00000000000..2bdd0ca42ca --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class ProviderIT implements CategorizedTestCase { + + RestOperations template = new RestTemplate(); + + private static final Logger LOGGER = LoggerFactory.getLogger(ProviderIT.class); + + @Override + public void testRestTransport() throws Exception { + + User user = getUser("Application/json"); + TestMgr.check(1L, user.getId()); + TestMgr.check("czd", user.getName()); + + User user2 = getUser("application/json"); + TestMgr.check(1L, user2.getId()); + TestMgr.check("czd", user2.getName()); + + User user3 = getUser("APPLICATION/JSON"); + TestMgr.check(1L, user3.getId()); + TestMgr.check("czd", user3.getName()); + } + + private User getUser(String contentType) throws IOException { + HttpHeaders headers = new HttpHeaders(); + headers.set("Content-Type", contentType); + + String requestBody = """ + { + "id": 1, + "name": "czd" + } + """; + + HttpEntity entity = new HttpEntity<>(requestBody, headers); + + String url = Config.GATEWAY_URL + "/testContentType"; + ResponseEntity response = template.exchange( + url, HttpMethod.POST, entity, String.class); + + User user = JsonUtils.readValue(response.getBody().getBytes(StandardCharsets.UTF_8), User.class); + return user; + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java new file mode 100644 index 00000000000..488a6cf712a --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient.Model; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component +public class ReactiveStreamIT implements CategorizedTestCase { + @Autowired + @Qualifier("reactiveStreamProvider") + ReactiveStreamClient reactiveStreamProvider; + + @Autowired + @Qualifier("reactiveStreamGateway") + ReactiveStreamClient reactiveStreamGateway; + + @Override + public void testRestTransport() throws Exception { + testSseString(reactiveStreamProvider); + testSseModel(reactiveStreamProvider); + testSseString(reactiveStreamGateway); + testSseModel(reactiveStreamGateway); + } + + private void testSseModel(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseModel(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(Model s) { + buffer.append(s.getName()).append(s.getAge()); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("jack0jack1jack2jack3jack4", buffer.toString()); + } + + private void testSseString(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseString(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(String s) { + buffer.append(s); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("abc", buffer.toString()); + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java new file mode 100644 index 00000000000..26a2a491bbe --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.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.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCaseRunner; +import org.apache.servicecomb.demo.TestMgr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class TestClientApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); + + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args); + + run(); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + } + + public static void run() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java new file mode 100644 index 00000000000..3ee5db8c346 --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.localregistry.RegistryBean; +import org.apache.servicecomb.localregistry.RegistryBean.Instance; +import org.apache.servicecomb.localregistry.RegistryBean.Instances; +import org.apache.servicecomb.provider.pojo.Invoker; +import org.reactivestreams.Publisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.WebSocket; + +@Configuration +public class ThirdSvcConfiguration { + @RequestMapping(path = "/ws") + public interface WebsocketClient { + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + WebSocket websocket(); + } + + @RequestMapping(path = "/") + public interface ReactiveStreamClient { + class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + Publisher sseString(); + + @GetMapping("/sseModel") + Publisher sseModel(); + } + + @Bean + public RegistryBean providerServiceBean() { + return new RegistryBean() + .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) + .setAppId("demo-etcd") + .setServiceName("provider") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of( + new Instance().setEndpoints(List.of("rest://localhost:9094"))))); + } + + @Bean + public RegistryBean gatewayServiceBean() { + return new RegistryBean() + .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) + .addSchemaInterface("WebsocketController", WebsocketClient.class) + .setAppId("demo-etcd") + .setServiceName("gateway") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of( + new Instance().setEndpoints(List.of("rest://localhost:9090?websocketEnabled=true"))))); + } + + @Bean("reactiveStreamProvider") + public ReactiveStreamClient reactiveStreamProvider() { + return Invoker.createProxy("provider", "ReactiveStreamController", ReactiveStreamClient.class); + } + + @Bean("reactiveStreamGateway") + public ReactiveStreamClient reactiveStreamGateway() { + return Invoker.createProxy("gateway", "ReactiveStreamController", ReactiveStreamClient.class); + } + + @Bean + public WebsocketClient gatewayWebsocketClient() { + return Invoker.createProxy("gateway", "WebsocketController", WebsocketClient.class); + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/User.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/User.java new file mode 100644 index 00000000000..ff421930ef2 --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/User.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.servicecomb.samples; + + +public class User { + + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java new file mode 100644 index 00000000000..ea7a7f45b15 --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.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.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.WebsocketClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import io.vertx.core.http.WebSocket; + +@Component +public class WebsocketIT implements CategorizedTestCase { + @Autowired + private WebsocketClient websocketClient; + + @Override + public void testRestTransport() throws Exception { + StringBuffer sb = new StringBuffer(); + AtomicBoolean closed = new AtomicBoolean(false); + CountDownLatch latch = new CountDownLatch(1); + + WebSocket webSocket = websocketClient.websocket(); + webSocket.textMessageHandler(s -> { + sb.append(s); + sb.append(" "); + webSocket.writeTextMessage(s); + }); + webSocket.closeHandler(v -> { + closed.set(true); + latch.countDown(); + }); + latch.await(30, TimeUnit.SECONDS); + TestMgr.check(sb.toString(), "hello hello 0 hello 1 hello 2 hello 3 hello 4 total 6 "); + TestMgr.check(closed.get(), true); + } +} diff --git a/demo/demo-etcd/test-client/src/main/resources/application.yml b/demo/demo-etcd/test-client/src/main/resources/application.yml new file mode 100644 index 00000000000..adcf259e984 --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-etcd + name: test-client + version: 0.0.1 + + rest: + address: 0.0.0.0:9097 # should be same with server.port to use web container + + config: + etcd: + instance-tag: tag1 + +test1: env +test2: applition +test3: service +test4: version +test5: tag diff --git a/demo/demo-etcd/test-client/src/main/resources/log4j2.xml b/demo/demo-etcd/test-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/EtcdIT.java b/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/EtcdIT.java new file mode 100644 index 00000000000..833feff87c0 --- /dev/null +++ b/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/EtcdIT.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.servicecomb.samples; + +import org.apache.servicecomb.demo.TestMgr; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = TestClientApplication.class) +public class EtcdIT { + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdIT.class); + + @BeforeEach + public void setUp() { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() { + try { + TestClientApplication.run(); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} diff --git a/demo/demo-filter/filter-client/pom.xml b/demo/demo-filter/filter-client/pom.xml index 908beb3ab8a..6c39fb4f5ad 100644 --- a/demo/demo-filter/filter-client/pom.xml +++ b/demo/demo-filter/filter-client/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-filter - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT filter-client Java Chassis::Demo::Filter::Client @@ -33,10 +33,6 @@ org.apache.servicecomb.demo demo-schema - - org.apache.servicecomb - solution-basic - org.apache.servicecomb foundation-test-scaffolding @@ -60,28 +56,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/FilterClient.java b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/FilterClient.java index 94e3883f5a7..ab454f7c9e2 100644 --- a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/FilterClient.java +++ b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/FilterClient.java @@ -19,7 +19,6 @@ import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; @@ -27,7 +26,6 @@ import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class FilterClient { private static final Logger LOGGER = LoggerFactory.getLogger(FilterClient.class); @@ -46,6 +44,6 @@ public static void main(String[] args) throws Exception { } public static void run() throws Exception { - CategorizedTestCaseRunner.runCategorizedTestCase("filterServer"); + CategorizedTestCaseRunner.runCategorizedTestCase("com.servicecomb.filterServer"); } } diff --git a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/ClientExceptionSchema.java b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/ClientExceptionSchema.java index 55065d5fb15..3d64b25b426 100644 --- a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/ClientExceptionSchema.java +++ b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/ClientExceptionSchema.java @@ -25,7 +25,7 @@ import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @RestSchema(schemaId = "ClientExceptionSchema") @RequestMapping(path = "/exception", produces = MediaType.APPLICATION_JSON_VALUE) @@ -38,9 +38,9 @@ interface IExceptionSchema { private IExceptionSchema exceptionSchema; - private RestTemplate restTemplate = RestTemplateBuilder.create(); + private RestOperations restTemplate = RestTemplateBuilder.create(); - @RpcReference(microserviceName = "filterServer", schemaId = "ExceptionSchema") + @RpcReference(microserviceName = "com.servicecomb.filterServer", schemaId = "ExceptionSchema") public void setExceptionSchema(IExceptionSchema exceptionSchema) { this.exceptionSchema = exceptionSchema; } @@ -48,7 +48,7 @@ public void setExceptionSchema(IExceptionSchema exceptionSchema) { @GetMapping(path = "/blockingExceptionRestTemplate") public boolean blockingExceptionRestTemplate() { return restTemplate.getForObject( - "servicecomb://filterServer/exception/blockingException", boolean.class); + "servicecomb://com.servicecomb.filterServer/exception/blockingException", boolean.class); } @GetMapping(path = "/blockingExceptionReference") @@ -58,7 +58,7 @@ public boolean blockingExceptionReference() { @GetMapping(path = "/blockingExceptionInvoker") public boolean blockingExceptionInvoker() { - return InvokerUtils.syncInvoke("filterServer", + return InvokerUtils.syncInvoke("com.servicecomb.filterServer", "ExceptionSchema", "blockingException", null, boolean.class); } diff --git a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/GovernanceConsumerSchema.java b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/GovernanceConsumerSchema.java new file mode 100644 index 00000000000..733d3ab1f54 --- /dev/null +++ b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/GovernanceConsumerSchema.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.servicecomb.demo.filter.client; + +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "GovernanceConsumerSchema") +@RequestMapping(path = "/govern", produces = MediaType.APPLICATION_JSON_VALUE) +public class GovernanceConsumerSchema { + interface GovernanceProviderSchemaInf { + boolean providerFlowControl(); + } + + @RpcReference(microserviceName = "com.servicecomb.filterServer", schemaId = "GovernanceProviderSchema") + private GovernanceProviderSchemaInf retrySchemaInf; + + + @GetMapping(path = "/edgeFlowControl") + public boolean edgeFlowControl() { + return true; + } + + @GetMapping(path = "/providerFlowControl") + public boolean providerFlowControl() { + return retrySchemaInf.providerFlowControl(); + } +} diff --git a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java index fd2ab36cdcc..159c52fe340 100644 --- a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java +++ b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java @@ -17,14 +17,18 @@ package org.apache.servicecomb.demo.filter.client; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; + +import jakarta.ws.rs.core.Response.Status; @RestSchema(schemaId = "RetryClientSchema") @RequestMapping(path = "/retry", produces = MediaType.APPLICATION_JSON_VALUE) @@ -33,14 +37,35 @@ interface RetrySchemaInf { CompletableFuture successWhenRetryAsync(); } - @RpcReference(microserviceName = "filterServer", schemaId = "RetrySchema") + @RpcReference(microserviceName = "com.servicecomb.filterServer", schemaId = "RetrySchema") private RetrySchemaInf retrySchemaInf; - RestTemplate restTemplate = RestTemplateBuilder.create(); + RestOperations restTemplate = RestTemplateBuilder.create(); + + private AtomicLong counter = new AtomicLong(0); + + @GetMapping(path = "/governance/edgeSuccessWhenRetry") + public boolean edgeSuccessWhenRetry() { + if (counter.getAndIncrement() % 3 != 0) { + throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "try again later."); + } + return true; + } + + @GetMapping(path = "/governance/edgeSuccessWhenRetryAsync") + public CompletableFuture edgeSuccessWhenRetryAsync() { + CompletableFuture result = new CompletableFuture<>(); + if (counter.getAndIncrement() % 2 == 0) { + result.completeExceptionally(new InvocationException(Status.INTERNAL_SERVER_ERROR, "try again later.")); + } else { + result.complete(true); + } + return result; + } @GetMapping(path = "/governance/successWhenRetry") public boolean successWhenRetry() { - return restTemplate.getForObject("servicecomb://filterServer/retry/governance/successWhenRetry", + return restTemplate.getForObject("servicecomb://com.servicecomb.filterServer/retry/governance/successWhenRetry", Boolean.class); } diff --git a/demo/demo-filter/filter-client/src/main/resources/microservice.yaml b/demo/demo-filter/filter-client/src/main/resources/microservice.yaml index 267d727bcd0..72f90936c83 100644 --- a/demo/demo-filter/filter-client/src/main/resources/microservice.yaml +++ b/demo/demo-filter/filter-client/src/main/resources/microservice.yaml @@ -18,23 +18,13 @@ servicecomb: service: application: filtertest - name: filterClient + # test service name with dot + name: com.servicecomb.filterClient version: 0.0.1 - registry: + registry: + sc: address: http://127.0.0.1:30100 - autodiscovery: true - client: - timeout: - idle: 60 - watch: 60 - instance: - healthCheck: - interval: 3 - pull: - interval: 90 - watch: true - rest: address: 0.0.0.0:8082 server: @@ -51,6 +41,7 @@ servicecomb: matches: - apiPath: prefix: "/retry/governance/" + retry: retry-governance: | maxAttempts: 2 diff --git a/demo/demo-filter/filter-edge/pom.xml b/demo/demo-filter/filter-edge/pom.xml index 3c96c3debf9..798f7b7697e 100644 --- a/demo/demo-filter/filter-edge/pom.xml +++ b/demo/demo-filter/filter-edge/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-filter - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT filter-edge Java Chassis::Demo::Filter::Edge @@ -33,10 +33,6 @@ org.apache.servicecomb.demo demo-schema - - org.apache.servicecomb - solution-basic - org.apache.servicecomb edge-core @@ -64,28 +60,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-filter/filter-edge/src/main/java/org/apache/servicecomb/demo/filter/FilterEdge.java b/demo/demo-filter/filter-edge/src/main/java/org/apache/servicecomb/demo/filter/FilterEdge.java index be3904a5362..8ea4b7cb22d 100644 --- a/demo/demo-filter/filter-edge/src/main/java/org/apache/servicecomb/demo/filter/FilterEdge.java +++ b/demo/demo-filter/filter-edge/src/main/java/org/apache/servicecomb/demo/filter/FilterEdge.java @@ -19,7 +19,6 @@ import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; @@ -27,7 +26,6 @@ import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class FilterEdge { private static final Logger LOGGER = LoggerFactory.getLogger(FilterEdge.class); diff --git a/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml b/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml index 5a0b4b648fd..128a825e6d6 100644 --- a/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml +++ b/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml @@ -18,28 +18,24 @@ servicecomb: service: application: filtertest - name: filterEdge + # test service name with dot + name: com.servicecomb.filterEdge version: 0.0.1 - registry: + registry: + sc: address: http://127.0.0.1:30100 - autodiscovery: true - client: - timeout: - idle: 60 - watch: 60 - instance: - healthCheck: - interval: 3 - pull: - interval: 90 - watch: true rest: address: 0.0.0.0:9090 server: compression: true + edge: + filter: + addHeader: + allowedHeaders: X-B3-TraceId + invocation: exception: print-stack-trace: true @@ -58,4 +54,26 @@ servicecomb: filterClient: prefixSegmentCount: 0 path: "/retry/.*" - microserviceName: filterClient + microserviceName: com.servicecomb.filterClient + filterClient2: + prefixSegmentCount: 0 + path: "/govern/.*" + microserviceName: com.servicecomb.filterClient + matchGroup: + edgeFlowControl: | + matches: + - apiPath: + exact: "/govern/edgeFlowControl" + retry-governance: | + matches: + - apiPath: + prefix: "/retry/governance/" + rateLimiting: + edgeFlowControl: | + timeoutDuration: 0 + limitRefreshPeriod: 1000 + rate: 1 + retry: + retry-governance: | + maxAttempts: 2 + retryOnResponseStatus: [500] diff --git a/demo/demo-filter/filter-server/pom.xml b/demo/demo-filter/filter-server/pom.xml index 1de38aa17ef..b3f257fd875 100644 --- a/demo/demo-filter/filter-server/pom.xml +++ b/demo/demo-filter/filter-server/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-filter - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT filter-server Java Chassis::Demo::Filter::Server @@ -33,10 +33,6 @@ org.apache.servicecomb.demo demo-schema - - org.apache.servicecomb - solution-basic - @@ -55,28 +51,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/FilterServer.java b/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/FilterServer.java index cf3d8779553..c947b0e7d0b 100644 --- a/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/FilterServer.java +++ b/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/FilterServer.java @@ -19,7 +19,6 @@ import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; @@ -27,7 +26,6 @@ import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class FilterServer { private static final Logger LOGGER = LoggerFactory.getLogger(FilterServer.class); diff --git a/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/ExceptionSchema.java b/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/ExceptionSchema.java index 4ae8b3b3947..980681b6b96 100644 --- a/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/ExceptionSchema.java +++ b/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/ExceptionSchema.java @@ -18,7 +18,7 @@ import java.util.concurrent.CompletableFuture; -import javax.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; diff --git a/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/GovernanceProviderSchema.java b/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/GovernanceProviderSchema.java new file mode 100644 index 00000000000..c2b5f37cadf --- /dev/null +++ b/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/GovernanceProviderSchema.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.filter.server; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "GovernanceProviderSchema") +@RequestMapping(path = "/govern", produces = MediaType.APPLICATION_JSON_VALUE) +public class GovernanceProviderSchema { + @GetMapping(path = "/providerFlowControl") + public boolean providerFlowControl() { + return true; + } +} diff --git a/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/RetrySchema.java b/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/RetrySchema.java index 3373e19bef4..603f2a79e55 100644 --- a/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/RetrySchema.java +++ b/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/RetrySchema.java @@ -19,7 +19,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicLong; -import javax.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; diff --git a/demo/demo-filter/filter-server/src/main/resources/microservice.yaml b/demo/demo-filter/filter-server/src/main/resources/microservice.yaml index ee573cbad46..bb96cf5f576 100644 --- a/demo/demo-filter/filter-server/src/main/resources/microservice.yaml +++ b/demo/demo-filter/filter-server/src/main/resources/microservice.yaml @@ -18,23 +18,13 @@ servicecomb: service: application: filtertest - name: filterServer + # test service name with dot + name: com.servicecomb.filterServer version: 0.0.1 - registry: - registerPath: true + registry: + sc: address: http://127.0.0.1:30100 - client: - timeout: - idle: 60 - watch: 60 - instance: - healthCheck: - interval: 3 - pull: - interval: 10 - watch: true - autodiscovery: true rest: address: 0.0.0.0:8080 @@ -43,6 +33,17 @@ servicecomb: highway: address: 0.0.0.0:7070 + matchGroup: + providerFlowControl: | + matches: + - apiPath: + exact: "/govern/providerFlowControl" + rateLimiting: + providerFlowControl: | + timeoutDuration: 0 + limitRefreshPeriod: 1000 + rate: 1 + #########SSL options ssl.protocols: TLSv1.2 ssl.authPeer: true @@ -59,4 +60,3 @@ ssl.crl: revoke.crl ssl.sslCustomClass: org.apache.servicecomb.demo.DemoSSLCustom vertx.disableFileCPResolving: false # false: create the .vertx directory, true: do not create - diff --git a/demo/demo-filter/filter-tests/pom.xml b/demo/demo-filter/filter-tests/pom.xml index 57edb3dd238..3936a85f517 100644 --- a/demo/demo-filter/filter-tests/pom.xml +++ b/demo/demo-filter/filter-tests/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-filter - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT filter-tests Java Chassis::Demo::Filter::Tests @@ -37,14 +37,6 @@ org.apache.servicecomb registry-local - - org.apache.servicecomb - solution-basic - - - org.apache.servicecomb - foundation-test-scaffolding - @@ -88,7 +80,7 @@ alias - -Dservicecomb.service.registry.address=http://service30100:30100 + -Dservicecomb.registry.sc.address=http://service30100:30100 /maven/maven/filter-server-${project.version}.jar @@ -96,7 +88,7 @@ service30100:service30100 - Register microservice instance success + ServiceComb is ready 8080 @@ -116,7 +108,7 @@ alias - -Dservicecomb.service.registry.address=http://service30100:30100 + -Dservicecomb.registry.sc.address=http://service30100:30100 /maven/maven/filter-client-${project.version}.jar @@ -124,7 +116,7 @@ service30100:service30100 - Register microservice instance success + ServiceComb is ready 8082 @@ -144,8 +136,7 @@ alias - -Dservicecomb.service.registry.address=http://service30100:30100 - -Dservicecomb.service.publishAddress=${docker.hostname} + -Dservicecomb.registry.sc.address=http://service30100:30100 /maven/maven/filter-edge-${project.version}.jar @@ -153,7 +144,7 @@ service30100:service30100 - Register microservice instance success + ServiceComb is ready 9090 @@ -193,19 +184,6 @@ io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/FilterTests.java b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/FilterTests.java index 167e5a3c919..0c7551b685d 100644 --- a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/FilterTests.java +++ b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/FilterTests.java @@ -19,7 +19,6 @@ import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; @@ -27,7 +26,6 @@ import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class FilterTests { private static final Logger LOGGER = LoggerFactory.getLogger(FilterTests.class); diff --git a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestExceptionSchemaFromClient.java b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestExceptionSchemaFromClient.java index 9287f698baf..e404b38b8a9 100644 --- a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestExceptionSchemaFromClient.java +++ b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestExceptionSchemaFromClient.java @@ -22,14 +22,14 @@ import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class TestExceptionSchemaFromClient implements CategorizedTestCase { - RestTemplate restTemplate = RestTemplateBuilder.create(); + RestOperations restTemplate = RestTemplateBuilder.create(); - private static final String SERVER = "servicecomb://filterClient"; + private static final String SERVER = "servicecomb://com.servicecomb.filterClient"; @Override public String getMicroserviceName() { diff --git a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestGovernanceSchemaFromEdge.java b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestGovernanceSchemaFromEdge.java new file mode 100644 index 00000000000..cd9958b7624 --- /dev/null +++ b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestGovernanceSchemaFromEdge.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.servicecomb.demo.filter.tests; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component +public class TestGovernanceSchemaFromEdge implements CategorizedTestCase { + interface GovernanceEdgeSchemaInf { + boolean edgeFlowControl(); + + boolean providerFlowControl(); + } + + @RpcReference(microserviceName = "com.servicecomb.filterEdge", schemaId = "GovernanceConsumerSchema") + private GovernanceEdgeSchemaInf retrySchemaInf; + + @Override + public String getMicroserviceName() { + return "com.servicecomb.filterEdge"; + } + + @Override + public void testRestTransport() throws Exception { + testEdgeFlowControl(); + testConsumerFlowControl(); + } + + private void testConsumerFlowControl() throws Exception { + CountDownLatch latch = new CountDownLatch(100); + AtomicBoolean expectedFailed = new AtomicBoolean(false); + AtomicBoolean notExpectedFailed = new AtomicBoolean(false); + + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + String name = "t-" + i + "-" + j; + new Thread(name) { + public void run() { + try { + boolean result = retrySchemaInf.providerFlowControl(); + if (!result) { + notExpectedFailed.set(true); + } + } catch (Exception e) { + if (!e.getMessage().contains("rate limited")) { + notExpectedFailed.set(true); + } + expectedFailed.set(true); + } + latch.countDown(); + } + }.start(); + } + Thread.sleep(100); + } + + latch.await(20, TimeUnit.SECONDS); + TestMgr.check(expectedFailed.get(), true); + TestMgr.check(notExpectedFailed.get(), false); + } + + private void testEdgeFlowControl() throws Exception { + CountDownLatch latch = new CountDownLatch(100); + AtomicBoolean expectedFailed = new AtomicBoolean(false); + AtomicBoolean notExpectedFailed = new AtomicBoolean(false); + + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + String name = "t-" + i + "-" + j; + new Thread(name) { + public void run() { + try { + boolean result = retrySchemaInf.edgeFlowControl(); + if (!result) { + notExpectedFailed.set(true); + } + } catch (Exception e) { + if (!e.getMessage().contains("rate limited")) { + notExpectedFailed.set(true); + } + expectedFailed.set(true); + } + latch.countDown(); + } + }.start(); + } + Thread.sleep(100); + } + + latch.await(20, TimeUnit.SECONDS); + TestMgr.check(expectedFailed.get(), true); + TestMgr.check(notExpectedFailed.get(), false); + } +} diff --git a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromClient.java b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromClient.java index c4f7c723c70..944f7831910 100644 --- a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromClient.java +++ b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromClient.java @@ -23,7 +23,7 @@ import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class TestRetrySchemaFromClient implements CategorizedTestCase { @@ -33,16 +33,16 @@ interface RetrySchemaInf { CompletableFuture successWhenRetryAsync(); } - @RpcReference(microserviceName = "filterClient", schemaId = "RetryClientSchema") + @RpcReference(microserviceName = "com.servicecomb.filterClient", schemaId = "RetryClientSchema") private RetrySchemaInf retrySchemaInf; - RestTemplate restTemplate = RestTemplateBuilder.create(); + RestOperations restTemplate = RestTemplateBuilder.create(); - private static final String SERVER = "servicecomb://filterClient"; + private static final String SERVER = "servicecomb://com.servicecomb.filterClient"; @Override public String getMicroserviceName() { - return "filterClient"; + return "com.servicecomb.filterClient"; } @Override diff --git a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.java b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.java index e98171e15fa..b28eaff3cbc 100644 --- a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.java +++ b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.java @@ -23,30 +23,35 @@ import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class TestRetrySchemaFromEdge implements CategorizedTestCase { interface RetrySchemaInf { + boolean edgeSuccessWhenRetry(); + boolean successWhenRetry(); + CompletableFuture edgeSuccessWhenRetryAsync(); + CompletableFuture successWhenRetryAsync(); } - @RpcReference(microserviceName = "filterEdge", schemaId = "RetryClientSchema") + @RpcReference(microserviceName = "com.servicecomb.filterEdge", schemaId = "RetryClientSchema") private RetrySchemaInf retrySchemaInf; - RestTemplate restTemplate = RestTemplateBuilder.create(); + RestOperations restTemplate = RestTemplateBuilder.create(); RestTemplate springRestTemplate = new RestTemplate(); - private static final String SERVER = "servicecomb://filterEdge"; + private static final String SERVER = "servicecomb://com.servicecomb.filterEdge"; private static final String EDGE_SERVER = "http://127.0.0.1:9090"; @Override public String getMicroserviceName() { - return "filterEdge"; + return "com.servicecomb.filterEdge"; } @Override @@ -54,6 +59,7 @@ public void testRestTransport() throws Exception { testRetryGovernanceFromEdgeDefaultDispatcher(); testRetryGovernanceRestTemplate(); testRetryGovernanceRpc(); + testEdgeRetryGovernanceRpc(); } private void testRetryGovernanceRpc() throws Exception { @@ -64,6 +70,14 @@ private void testRetryGovernanceRpc() throws Exception { TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true); } + private void testEdgeRetryGovernanceRpc() throws Exception { + TestMgr.check(retrySchemaInf.edgeSuccessWhenRetry(), true); + TestMgr.check(retrySchemaInf.edgeSuccessWhenRetry(), true); + + TestMgr.check(retrySchemaInf.edgeSuccessWhenRetryAsync().get(), true); + TestMgr.check(retrySchemaInf.edgeSuccessWhenRetryAsync().get(), true); + } + private void testRetryGovernanceRestTemplate() { TestMgr.check(restTemplate.getForObject( SERVER + "/retry/governance/successWhenRetry", boolean.class), true); @@ -73,8 +87,8 @@ private void testRetryGovernanceRestTemplate() { private void testRetryGovernanceFromEdgeDefaultDispatcher() { TestMgr.check(springRestTemplate.getForObject( - EDGE_SERVER + "/service/filterClient/retry/governance/successWhenRetry", boolean.class), true); + EDGE_SERVER + "/service/com.servicecomb.filterClient/retry/governance/successWhenRetry", boolean.class), true); TestMgr.check(springRestTemplate.getForObject( - EDGE_SERVER + "/service/filterClient/retry/governance/successWhenRetry", boolean.class), true); + EDGE_SERVER + "/service/com.servicecomb.filterClient/retry/governance/successWhenRetry", boolean.class), true); } } diff --git a/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml b/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml index 42caced4b9d..5694d54ac56 100644 --- a/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml +++ b/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml @@ -18,33 +18,14 @@ servicecomb: service: application: filtertest - name: filterTests + # test service name with dot + name: com.servicecomb.filterTests version: 0.0.1 - registry: + registry: + sc: address: http://127.0.0.1:30100 - autodiscovery: true - client: - timeout: - idle: 60 - watch: 60 - instance: - healthCheck: - interval: 3 - pull: - interval: 90 - watch: true invocation: exception: print-stack-trace: true - # test governance retry - matchGroup: - retry-governance: | - matches: - - apiPath: - prefix: "/retry/governance/" - retry: - retry-governance: | - maxAttempts: 2 - retryOnResponseStatus: [500] diff --git a/demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/GovernanceConsumerSchema.yaml b/demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/GovernanceConsumerSchema.yaml new file mode 100644 index 00000000000..b29502270ab --- /dev/null +++ b/demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/GovernanceConsumerSchema.yaml @@ -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. +## --------------------------------------------------------------------------- +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.demo.filter.client.GovernanceConsumerSchema + version: 1.0.0 +servers: +- url: /govern +paths: + /edgeFlowControl: + get: + operationId: edgeFlowControl + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: boolean + /providerFlowControl: + get: + operationId: providerFlowControl + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: boolean +components: {} diff --git a/demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/RetryClientSchema.yaml b/demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/RetryClientSchema.yaml new file mode 100644 index 00000000000..77517becf68 --- /dev/null +++ b/demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/RetryClientSchema.yaml @@ -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. +## --------------------------------------------------------------------------- +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.demo.filter.client.RetryClientSchema + version: 1.0.0 +servers: +- url: /retry +paths: + /governance/edgeSuccessWhenRetry: + get: + operationId: edgeSuccessWhenRetry + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: boolean + /governance/edgeSuccessWhenRetryAsync: + get: + operationId: edgeSuccessWhenRetryAsync + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: boolean + /governance/successWhenRetry: + get: + operationId: successWhenRetry + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: boolean + /governance/successWhenRetryAsync: + get: + operationId: successWhenRetryAsync + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: boolean +components: {} diff --git a/demo/demo-filter/filter-tests/src/main/resources/microservices/filterEdge/RetryClientSchema.yaml b/demo/demo-filter/filter-tests/src/main/resources/microservices/filterEdge/RetryClientSchema.yaml deleted file mode 100644 index 95b4d5e0f17..00000000000 --- a/demo/demo-filter/filter-tests/src/main/resources/microservices/filterEdge/RetryClientSchema.yaml +++ /dev/null @@ -1,48 +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. -## --------------------------------------------------------------------------- ---- -swagger: "2.0" -info: - version: "1.0.0" - title: "swagger definition for org.apache.servicecomb.demo.filter.retry.RetryClientSchema" - x-java-interface: "gen.swagger.RetryClientSchemaIntf" -basePath: "/retry" -schemes: -- "http" -consumes: -- "application/json" -produces: -- "application/json" -paths: - /governance/successWhenRetry: - get: - operationId: "successWhenRetry" - parameters: [] - responses: - "200": - description: "response of 200" - schema: - type: "boolean" - /governance/successWhenRetryAsync: - get: - operationId: "successWhenRetryAsync" - parameters: [] - responses: - "200": - description: "response of 200" - schema: - type: "boolean" diff --git a/demo/demo-filter/filter-tests/src/main/resources/registry.yaml b/demo/demo-filter/filter-tests/src/main/resources/registry.yaml index caad0222370..1e09e18eea2 100644 --- a/demo/demo-filter/filter-tests/src/main/resources/registry.yaml +++ b/demo/demo-filter/filter-tests/src/main/resources/registry.yaml @@ -14,12 +14,10 @@ ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- -filterEdge: +com.servicecomb.filterEdge: - id: "001" version: "1.0" appid: filtertest - schemaIds: - - RetryClientSchema instances: - endpoints: - rest://127.0.0.1:9090 diff --git a/demo/demo-filter/pom.xml b/demo/demo-filter/pom.xml index ba46509fe1f..45e0206faf1 100644 --- a/demo/demo-filter/pom.xml +++ b/demo/demo-filter/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-filter Java Chassis::Demo::Filter @@ -34,6 +34,10 @@ filter-tests + + org.apache.servicecomb + solution-basic + org.apache.servicecomb registry-service-center diff --git a/demo/demo-jaxrs/jaxrs-client/pom.xml b/demo/demo-jaxrs/jaxrs-client/pom.xml index b0dc92551c4..d25a3cdecca 100644 --- a/demo/demo-jaxrs/jaxrs-client/pom.xml +++ b/demo/demo-jaxrs/jaxrs-client/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-jaxrs - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT jaxrs-client Java Chassis::Demo::JAXRS::Client @@ -33,22 +33,6 @@ org.apache.servicecomb.demo demo-schema - - org.apache.servicecomb - provider-jaxrs - - - org.apache.servicecomb - provider-springmvc - - - org.apache.servicecomb - provider-pojo - - - org.apache.servicecomb - handler-governance - @@ -61,24 +45,88 @@ jaxrs-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsClient.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsClient.java index 0a2a357ba09..2428e654774 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsClient.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsClient.java @@ -21,11 +21,9 @@ import java.util.List; import java.util.Map; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response.Status; - import org.apache.http.HttpStatus; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.CodeFirstRestTemplate; import org.apache.servicecomb.demo.DemoConst; @@ -37,9 +35,8 @@ import org.apache.servicecomb.demo.jaxrs.client.pojoDefault.DefaultModelServiceClient; import org.apache.servicecomb.demo.jaxrs.client.validation.ValidationServiceClient; import org.apache.servicecomb.demo.validator.Student; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; @@ -53,14 +50,16 @@ import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; + +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response.Status; @SpringBootApplication -@EnableServiceComb public class JaxrsClient { private static final Logger LOGGER = LoggerFactory.getLogger(JaxrsClient.class); - private static RestTemplate templateNew = RestTemplateBuilder.create(); + private static RestOperations templateNew = RestTemplateBuilder.create(); public static void main(String[] args) throws Exception { new SpringApplicationBuilder(JaxrsClient.class).web(WebApplicationType.NONE).run(args); @@ -77,6 +76,7 @@ public static void main(String[] args) throws Exception { } TestMgr.summary(); + LOGGER.info("-------------- last time updated checks(maybe more/less): 540 -------------"); } public static void init() throws Exception { @@ -87,7 +87,7 @@ public static void init() throws Exception { public static void run() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("jaxrs"); - CodeFirstRestTemplate codeFirstClient = new CodeFirstRestTemplateJaxrs(); + CodeFirstRestTemplate codeFirstClient = BeanUtils.getBean(CodeFirstRestTemplateJaxrs.class); codeFirstClient.testCodeFirst(templateNew, "jaxrs", "/codeFirstJaxrs/"); testCompute(templateNew); testValidator(templateNew); @@ -100,19 +100,19 @@ public static void run() throws Exception { testOnlyRest(templateNew); } - private static void testOnlyRest(RestTemplate template) { + private static void testOnlyRest(RestOperations template) { String microserviceName = "jaxrs"; String cseUrlPrefix = "cse://" + microserviceName; - ArchaiusUtils.setProperty("servicecomb.references.transport." + microserviceName, "rest"); + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, "rest"); testGetRest(template, cseUrlPrefix); testSpringMvcDefaultValuesJavaPrimitiveRest(templateNew); } - private static void testCompute(RestTemplate template) throws Exception { + private static void testCompute(RestOperations template) throws Exception { String microserviceName = "jaxrs"; for (String transport : DemoConst.transports) { - ArchaiusUtils.setProperty("servicecomb.references.transport." + microserviceName, transport); + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); String cseUrlPrefix = "cse://" + microserviceName; @@ -126,10 +126,10 @@ private static void testCompute(RestTemplate template) throws Exception { } } - private static void testValidator(RestTemplate template) { + private static void testValidator(RestOperations template) { String microserviceName = "jaxrs"; for (String transport : DemoConst.transports) { - ArchaiusUtils.setProperty("servicecomb.references.transport." + microserviceName, transport); + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); String cseUrlPrefix = "cse://" + microserviceName + "/validator/"; @@ -150,7 +150,7 @@ private static void testValidator(RestTemplate template) { } } - private static void testJaxRSDefaultValuesRest(RestTemplate template) { + private static void testJaxRSDefaultValuesRest(RestOperations template) { String result; String microserviceName = "jaxrs"; String cseUrlPrefix = "cse://" + microserviceName + "/JaxRSDefaultValues/"; @@ -193,10 +193,10 @@ private static void testJaxRSDefaultValuesRest(RestTemplate template) { TestMgr.check(failed, true); } - private static void testJaxRSDefaultValuesAllTransport(RestTemplate template) { + private static void testJaxRSDefaultValuesAllTransport(RestOperations template) { String microserviceName = "jaxrs"; for (String transport : DemoConst.transports) { - ArchaiusUtils.setProperty("servicecomb.references.transport." + microserviceName, transport); + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); String cseUrlPrefix = "cse://" + microserviceName + "/JaxRSDefaultValues/"; @@ -207,7 +207,9 @@ private static void testJaxRSDefaultValuesAllTransport(RestTemplate template) { MultiValueMap map = new LinkedMultiValueMap<>(); HttpEntity> request = new HttpEntity<>(map, headers); String result = template.postForObject(cseUrlPrefix + "/form", request, String.class); - TestMgr.check("Hello 20bobo", result); + // TODO: do not support form parameters default value + // TestMgr.check("Hello 20bobo", result); + TestMgr.check("Hello 0null", result); headers = new HttpHeaders(); HttpEntity entity = new HttpEntity<>(null, headers); @@ -286,7 +288,7 @@ private static void testJaxRSDefaultValuesAllTransport(RestTemplate template) { } } - private static void testGetRest(RestTemplate template, String cseUrlPrefix) { + private static void testGetRest(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "3"); @@ -302,7 +304,7 @@ private static void testGetRest(RestTemplate template, String cseUrlPrefix) { TestMgr.check(-1, result); } - private static void testGetAllTransport(RestTemplate template, String cseUrlPrefix) { + private static void testGetAllTransport(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "3"); @@ -318,7 +320,7 @@ private static void testGetAllTransport(RestTemplate template, String cseUrlPref TestMgr.check(-1, result); } - private static void testPost(RestTemplate template, String cseUrlPrefix) { + private static void testPost(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "3"); @@ -339,17 +341,17 @@ private static void testPost(RestTemplate template, String cseUrlPrefix) { template.postForObject(cseUrlPrefix + "/compute/saysomething", reqEntity, String.class)); } - private static void testPut(RestTemplate template, String cseUrlPrefix) { + private static void testPut(RestOperations template, String cseUrlPrefix) { template.put(cseUrlPrefix + "/compute/sayhi/{name}", null, "world"); } - private static void testDelete(RestTemplate template, String cseUrlPrefix) { + private static void testDelete(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("name", "world"); template.delete(cseUrlPrefix + "/compute/sayhei/?name={name}", params); } - private static void testExchange(RestTemplate template, String cseUrlPrefix) { + private static void testExchange(RestOperations template, String cseUrlPrefix) { HttpHeaders headers = new HttpHeaders(); headers.add("Accept", MediaType.APPLICATION_JSON); Person person = new Person(); @@ -366,7 +368,7 @@ private static void testExchange(RestTemplate template, String cseUrlPrefix) { TestMgr.check("abcdef", resEntity2.getBody()); } - private static void testRawJsonParam(RestTemplate template, String cseUrlPrefix) throws Exception { + private static void testRawJsonParam(RestOperations template, String cseUrlPrefix) throws Exception { Map person = new HashMap<>(); person.put("name", "Tom"); String jsonPerson = RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(person); @@ -375,7 +377,7 @@ private static void testRawJsonParam(RestTemplate template, String cseUrlPrefix) } @SuppressWarnings({"rawtypes"}) - private static void testValidatorAddFail(RestTemplate template, String cseUrlPrefix) { + private static void testValidatorAddFail(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "3"); @@ -386,8 +388,8 @@ private static void testValidatorAddFail(RestTemplate template, String cseUrlPre isExcep = true; TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); - // Message dependends on locale, so just check the short part. - // 'must be greater than or equal to 20', propertyPath=add.arg1, rootBeanClass=class org.apache.servicecomb.demo.jaxrs.server.Validator, messageTemplate='{javax.validation.constraints.Min.message}'}]] + // Message depended on locale, so just check the short part. + // 'must be greater than or equal to 20', propertyPath=add.arg1, rootBeanClass=class org.apache.servicecomb.demo.jaxrs.server.Validator, messageTemplate='{jakarta.validation.constraints.Min.message}'}]] // ignored if (e.getErrorData() instanceof CommonExceptionData) { // highway decode/encode 'Object' with special type information, got runtime type @@ -407,7 +409,7 @@ private static void testValidatorAddFail(RestTemplate template, String cseUrlPre TestMgr.check(true, isExcep); } - private static void testValidatorAddSuccess(RestTemplate template, String cseUrlPrefix) { + private static void testValidatorAddSuccess(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "20"); @@ -416,7 +418,7 @@ private static void testValidatorAddSuccess(RestTemplate template, String cseUrl } @SuppressWarnings({"rawtypes"}) - private static void testValidatorSayHiFail(RestTemplate template, String cseUrlPrefix) { + private static void testValidatorSayHiFail(RestOperations template, String cseUrlPrefix) { boolean isExcep = false; try { template.exchange(cseUrlPrefix + "sayhi/{name}", HttpMethod.PUT, null, String.class, "te"); @@ -424,7 +426,7 @@ private static void testValidatorSayHiFail(RestTemplate template, String cseUrlP isExcep = true; TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); - // Message dependends on locale, so just check the short part. + // Message depended on locale, so just check the short part. if (e.getErrorData() instanceof CommonExceptionData) { // highway decode/encode 'Object' with special type information, got runtime type CommonExceptionData data = (CommonExceptionData) e.getErrorData(); @@ -441,15 +443,15 @@ private static void testValidatorSayHiFail(RestTemplate template, String cseUrlP TestMgr.check(true, isExcep); } - private static void testValidatorSayHiSuccess(RestTemplate template, String cseUrlPrefix) { + private static void testValidatorSayHiSuccess(RestOperations template, String cseUrlPrefix) { ResponseEntity responseEntity = template.exchange(cseUrlPrefix + "sayhi/{name}", HttpMethod.PUT, null, String.class, "world"); - TestMgr.check(202, responseEntity.getStatusCodeValue()); + TestMgr.check(202, responseEntity.getStatusCode().value()); TestMgr.check("world sayhi", responseEntity.getBody()); } @SuppressWarnings({"rawtypes"}) - private static void testValidatorExchangeFail(RestTemplate template, String cseUrlPrefix) { + private static void testValidatorExchangeFail(RestOperations template, String cseUrlPrefix) { HttpHeaders headers = new HttpHeaders(); headers.add("Accept", MediaType.APPLICATION_JSON); Student student = new Student(); @@ -466,7 +468,7 @@ private static void testValidatorExchangeFail(RestTemplate template, String cseU isExcep = true; TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); - // Message dependends on locale, so just check the short part. + // Message depended on locale, so just check the short part. if (e.getErrorData() instanceof CommonExceptionData) { // highway decode/encode 'Object' with special type information, got runtime type CommonExceptionData data = (CommonExceptionData) e.getErrorData(); @@ -483,7 +485,7 @@ private static void testValidatorExchangeFail(RestTemplate template, String cseU TestMgr.check(true, isExcep); } - private static void testValidatorExchangeSuccess(RestTemplate template, String cseUrlPrefix) { + private static void testValidatorExchangeSuccess(RestOperations template, String cseUrlPrefix) { Student student = new Student(); student.setName("test"); student.setAge(15); @@ -491,7 +493,7 @@ private static void testValidatorExchangeSuccess(RestTemplate template, String c TestMgr.check("hello test 15", result); } - private static void testSpringMvcDefaultValuesJavaPrimitiveRest(RestTemplate template) { + private static void testSpringMvcDefaultValuesJavaPrimitiveRest(RestOperations template) { String microserviceName = "jaxrs"; String cseUrlPrefix = "cse://" + microserviceName + "/JaxRSDefaultValues/"; @@ -502,7 +504,9 @@ private static void testSpringMvcDefaultValuesJavaPrimitiveRest(RestTemplate tem //default values with primitive String result = template.postForObject(cseUrlPrefix + "/javaprimitiveint", request, String.class); - TestMgr.check("Hello 0bobo", result); + // TODO: form default values support +// TestMgr.check("Hello 0bobo", result); + TestMgr.check("Hello 0null", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivenumber", request, String.class); TestMgr.check("Hello 0.0false", result); @@ -517,7 +521,7 @@ private static void testSpringMvcDefaultValuesJavaPrimitiveRest(RestTemplate tem TestMgr.check("Hello false,\0,0,0,0,0,0.0,0.0,null", result); } - private static void testSpringMvcDefaultValuesJavaPrimitiveAllTransport(RestTemplate template) { + private static void testSpringMvcDefaultValuesJavaPrimitiveAllTransport(RestOperations template) { String microserviceName = "jaxrs"; for (String transport : DemoConst.transports) { CategorizedTestCaseRunner.changeTransport(microserviceName, transport); @@ -531,7 +535,9 @@ private static void testSpringMvcDefaultValuesJavaPrimitiveAllTransport(RestTemp //default values with primitive String result = template.postForObject(cseUrlPrefix + "/javaprimitiveint", request, String.class); - TestMgr.check("Hello 0bobo", result); + // TODO: form default values support +// TestMgr.check("Hello 0bobo", result); + TestMgr.check("Hello 0null", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivenumber", request, String.class); TestMgr.check("Hello 0.0false", result); diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CodeFirstRestTemplateJaxrs.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CodeFirstRestTemplateJaxrs.java index 299c4ec72c9..66c5ab958b4 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CodeFirstRestTemplateJaxrs.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CodeFirstRestTemplateJaxrs.java @@ -17,107 +17,46 @@ package org.apache.servicecomb.demo.jaxrs.client; -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; - -import javax.ws.rs.core.MediaType; - -import org.apache.commons.io.FileUtils; import org.apache.servicecomb.demo.CodeFirstRestTemplate; import org.apache.servicecomb.demo.TestMgr; -import org.springframework.core.io.FileSystemResource; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Component; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; +@Component public class CodeFirstRestTemplateJaxrs extends CodeFirstRestTemplate { @Override - protected void testAllTransport(String microserviceName, RestTemplate template, String cseUrlPrefix) { + protected void testAllTransport(String microserviceName, RestOperations template, String cseUrlPrefix) { testDefaultPath(template, cseUrlPrefix); test404(template); super.testAllTransport(microserviceName, template, cseUrlPrefix); } - private void testDefaultPath(RestTemplate template, String cseUrlPrefix) { + private void testDefaultPath(RestOperations template, String cseUrlPrefix) { int result = template.getForObject(cseUrlPrefix.substring(0, cseUrlPrefix.length() - 1), Integer.class); TestMgr.check(100, result); } @Override - protected void testOnlyRest(String microserviceName, RestTemplate template, String cseUrlPrefix) { - try { - testUpload(template, cseUrlPrefix); - } catch (IOException e) { - throw new IllegalStateException(e); - } + protected void testOnlyRest(String microserviceName, RestOperations template, String cseUrlPrefix) { super.testOnlyRest(microserviceName, template, cseUrlPrefix); } - private void testUpload(RestTemplate template, String cseUrlPrefix) throws IOException { - String file1Content = "Hello World"; - File file1 = File.createTempFile("jaxrstest1", ".txt"); - FileUtils.writeStringToFile(file1, file1Content, StandardCharsets.UTF_8, false); - - testFileUpload(template, cseUrlPrefix, file1, file1Content); - testFileAndStringUpload(template, cseUrlPrefix, file1, file1Content); - } - - private void testFileUpload(RestTemplate template, String cseUrlPrefix, File file1, String file1Content) - throws IOException { - Map map = new HashMap<>(); - map.put("file1", new FileSystemResource(file1)); - String file2Content = "Hello EveryOne"; - File file2 = File.createTempFile("测试2", ".txt"); - FileUtils.writeStringToFile(file2, file2Content, StandardCharsets.UTF_8, false); - map.put("file2", new FileSystemResource(file2)); - - String result1 = template.postForObject(cseUrlPrefix + "/upload1", new HttpEntity<>(new HashMap<>()), String.class); - TestMgr.check("null file", result1); - - String expect = String.format("%s:%s:%s\n" + "%s:%s:%s", - file1.getName(), - MediaType.TEXT_PLAIN, - file1Content, - file2.getName(), - MediaType.TEXT_PLAIN, - file2Content); - String result2 = template.postForObject(cseUrlPrefix + "/upload1", new HttpEntity<>(map), String.class); - TestMgr.check(expect, result2); - } - - private void testFileAndStringUpload(RestTemplate template, String cseUrlPrefix, File file1, String file1Content) { - Map map = new HashMap<>(); - String message = "hi"; - map.put("file1", new FileSystemResource(file1)); - map.put("message", message); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA); - String expect = String.format("%s:%s:%s:%s", - file1.getName(), - MediaType.TEXT_PLAIN, - file1Content, - message); - String result = template.postForObject(cseUrlPrefix + "/upload2", new HttpEntity<>(map, headers), String.class); - TestMgr.check(expect, result); - } - private void test404(RestTemplate template) { + private void test404(RestOperations template) { HttpClientErrorException exception = null; try { template.getForEntity("http://127.0.0.1:8080/aPathNotExist", String.class); + TestMgr.check("expect throw but not", ""); } catch (RestClientException e) { if (e instanceof HttpClientErrorException) { exception = (HttpClientErrorException) e; } } - TestMgr.check(404, exception.getRawStatusCode()); - TestMgr.check("404 Not Found: \"{\"message\":\"Not Found\"}\"", exception.getMessage()); + TestMgr.check(404, exception.getStatusCode().value()); + TestMgr.check(true, exception.getMessage().contains("404 Not Found")); } } diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CustomLoadbalanceExtensionsFactory.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CustomLoadbalanceExtensionsFactory.java index 6e1ccf8fe32..9614164a05f 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CustomLoadbalanceExtensionsFactory.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CustomLoadbalanceExtensionsFactory.java @@ -22,9 +22,6 @@ import org.apache.servicecomb.loadbalance.RuleExt; import org.springframework.stereotype.Component; -import com.netflix.client.DefaultLoadBalancerRetryHandler; -import com.netflix.client.Utils; - @Component public class CustomLoadbalanceExtensionsFactory implements ExtensionsFactory { @@ -32,20 +29,6 @@ static class MyCustomRule extends RoundRobinRuleExt { } - static class MyCustomHandler extends DefaultLoadBalancerRetryHandler { - @Override - public boolean isRetriableException(Throwable e, boolean sameServer) { - if (retryEnabled) { - return Utils.isPresentAsCause(e, getRetriableExceptions()); - } - return false; - } - - public MyCustomHandler(int retrySameServer, int retryNextServer, boolean retryEnabled) { - super(retrySameServer, retryNextServer, retryEnabled); - } - } - @Override public boolean isSupport(String key, String value) { return (Configuration.RULE_STRATEGY_NAME.equals(key) && diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java index ad1d21cc04e..5eba5b94c95 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java @@ -19,8 +19,6 @@ import java.util.List; import java.util.Map; -import javax.ws.rs.core.Response.Status; - import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; @@ -28,33 +26,24 @@ import org.apache.servicecomb.demo.multiErrorCode.MultiResponse200; import org.apache.servicecomb.demo.multiErrorCode.MultiResponse400; import org.apache.servicecomb.demo.multiErrorCode.MultiResponse500; -import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; -import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.definition.DefinitionConst; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.HttpServerErrorException; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; import io.vertx.core.json.Json; import io.vertx.core.json.JsonObject; +import jakarta.ws.rs.core.Response.Status; @Component public class MultiErrorCodeServiceClient implements CategorizedTestCase { private static final String SERVER = "cse://jaxrs"; - private static String serverDirectURL; - - private static RestTemplate template = RestTemplateBuilder.create(); + private static RestOperations template = RestTemplateBuilder.create(); @Override public void testAllTransport() throws Exception { @@ -67,7 +56,6 @@ public void testAllTransport() throws Exception { @Override public void testRestTransport() throws Exception { - prepareServerDirectURL(); testErrorCodeWrongType(); } @@ -76,53 +64,39 @@ public void testHighwayTransport() throws Exception { } - private static void prepareServerDirectURL() { - Microservice microservice = RegistrationManager.INSTANCE.getMicroservice(); - MicroserviceInstance microserviceInstance = (MicroserviceInstance) DiscoveryManager.INSTANCE - .getAppManager() - .getOrCreateMicroserviceVersionRule(microservice.getAppId(), "jaxrs", DefinitionConst.VERSION_RULE_ALL) - .getVersionedCache() - .mapData() - .values() - .stream() - .findFirst() - .get(); - URIEndpointObject edgeAddress = new URIEndpointObject(microserviceInstance.getEndpoints().get(0)); - serverDirectURL = String.format("http://%s:%d/", edgeAddress.getHostOrIp(), edgeAddress.getPort()); - } private static void testErrorCodeWrongType() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); String body = "{\"message\":\"hello\",\"code\":\"wrongType\""; HttpEntity entity = new HttpEntity<>(body, headers); - ResponseEntity result; try { template - .postForEntity(serverDirectURL + "/MultiErrorCodeService/errorCode", entity, MultiResponse200.class); - } catch (HttpClientErrorException e) { - TestMgr.check(e.getRawStatusCode(), 400); - TestMgr.check(e.getMessage(), - "400 Bad Request: \"{\"message\":\"Parameter is not valid for operation [jaxrs.MultiErrorCodeService.errorCode]." + - " Parameter is [request]. Processor is [body].\"}\""); + .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", entity, MultiResponse200.class); + TestMgr.fail("expect failed."); + } catch (InvocationException e) { + TestMgr.check(e.getStatusCode(), 400); } entity = new HttpEntity<>(null, headers); try { - result = template - .postForEntity(serverDirectURL + "/MultiErrorCodeService/errorCode", entity, MultiResponse200.class); - TestMgr.check(590, 200); - } catch (HttpServerErrorException e) { - TestMgr.check(e.getRawStatusCode(), 500); + template + .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", entity, MultiResponse200.class); + TestMgr.fail("expect failed."); + } catch (InvocationException e) { + TestMgr.check(e.getStatusCode(), 400); } - // not recommend + // wrong type body = "{\"message\":\"hello\",\"code\":\"200\"}"; entity = new HttpEntity<>(body, headers); - result = template - .postForEntity(serverDirectURL + "/MultiErrorCodeService/errorCode", entity, MultiResponse200.class); - TestMgr.check(result.getStatusCodeValue(), 200); - TestMgr.check(result.getBody().getMessage(), "success result"); + try { + template + .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", entity, MultiResponse200.class); + TestMgr.fail("expect failed."); + } catch (InvocationException e) { + TestMgr.check(e.getStatusCode(), 400); + } } private static void testErrorCode() { @@ -131,7 +105,7 @@ private static void testErrorCode() { request.setCode(200); ResponseEntity result = template .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", request, MultiResponse200.class); - TestMgr.check(result.getStatusCodeValue(), 200); + TestMgr.check(result.getStatusCode().value(), 200); TestMgr.check(result.getBody().getMessage(), "success result"); request.setCode(400); @@ -161,7 +135,7 @@ private static void testErrorCodeWithHeader() { request.setCode(200); ResponseEntity result = template .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeader", request, MultiResponse200.class); - TestMgr.check(result.getStatusCodeValue(), 200); + TestMgr.check(result.getStatusCode().value(), 200); TestMgr.check(result.getBody().getMessage(), "success result"); TestMgr.check(result.getBody().getCode(), 200); TestMgr.check(result.getHeaders().getFirst("x-code"), 200); @@ -196,7 +170,7 @@ private static void testErrorCodeWithHeaderJAXRS() { request.setMessage("success result"); ResponseEntity result = template .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeaderJAXRS", request, MultiResponse200.class); - TestMgr.check(result.getStatusCodeValue(), 200); + TestMgr.check(result.getStatusCode().value(), 200); TestMgr.check(result.getBody().getMessage(), "success result"); TestMgr.check(result.getBody().getCode(), 200); TestMgr.check(result.getHeaders().getFirst("x-code"), 200); @@ -235,7 +209,7 @@ private static void testErrorCodeWithHeaderJAXRSUsingRowType() { ResponseEntity result = template .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeaderJAXRS", requestJson, MultiResponse200.class); - TestMgr.check(result.getStatusCodeValue(), 200); + TestMgr.check(result.getStatusCode().value(), 200); TestMgr.check(result.getBody().getMessage(), "test message"); TestMgr.check(result.getBody().getCode(), 200); TestMgr.check(result.getHeaders().getFirst("x-code"), 200); @@ -248,7 +222,7 @@ private static void testErrorCodeWithHeaderJAXRSUsingRowType() { result = template .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeaderJAXRS", new JsonObject(stringRequest), MultiResponse200.class); - TestMgr.check(result.getStatusCodeValue(), 200); + TestMgr.check(result.getStatusCode().value(), 200); TestMgr.check(result.getBody().getMessage(), "test message"); TestMgr.check(result.getBody().getCode(), 200); TestMgr.check(result.getHeaders().getFirst("x-code"), 200); @@ -262,7 +236,7 @@ private static void testNoClientErrorCode() { @SuppressWarnings("rawtypes") ResponseEntity listResult = template .postForEntity(SERVER + "/MultiErrorCodeService/noClientErrorCode", requestJson, List.class); - TestMgr.check(listResult.getStatusCodeValue(), 200); + TestMgr.check(listResult.getStatusCode().value(), 200); Map mapResult = RestObjectMapperFactory.getRestObjectMapper().convertValue(listResult.getBody().get(0), Map.class); TestMgr.check(mapResult.get("message"), "test message"); diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestClientTimeout.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestClientTimeout.java index 87293a3e5c2..7d8017eb38d 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestClientTimeout.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestClientTimeout.java @@ -26,17 +26,17 @@ import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class TestClientTimeout implements CategorizedTestCase { - private static RestTemplate template = RestTemplateBuilder.create(); + private static RestOperations template = RestTemplateBuilder.create(); public void testAllTransport() throws Exception { testClientTimeOut(template); } - private static void testClientTimeOut(RestTemplate template) { + private static void testClientTimeOut(RestOperations template) { String microserviceName = "jaxrs"; String cseUrlPrefix = "cse://" + microserviceName + "/clientreqtimeout/"; @@ -45,7 +45,7 @@ private static void testClientTimeOut(RestTemplate template) { testClientTimeoutAdd(template, cseUrlPrefix); } - private static void testClientTimeoutSayHi(RestTemplate template, String cseUrlPrefix) { + private static void testClientTimeoutSayHi(RestOperations template, String cseUrlPrefix) { Student student = new Student(); student.setName("timeout"); student.setAge(30); @@ -53,7 +53,7 @@ private static void testClientTimeoutSayHi(RestTemplate template, String cseUrlP TestMgr.check("hello timeout 30", result); } - private static void testClientTimeoutAdd(RestTemplate template, String cseUrlPrefix) { + private static void testClientTimeoutAdd(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "20"); diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFileUploadSchema.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFileUploadSchema.java new file mode 100644 index 00000000000..049da2e47f9 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFileUploadSchema.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.servicecomb.demo.jaxrs.client; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; + +import jakarta.ws.rs.core.MediaType; + +@Component +public class TestFileUploadSchema implements CategorizedTestCase { + @Override + public void testRestTransport() throws Exception { + testUpload(RestTemplateBuilder.create(), "servicecomb://jaxrs/fileUpload"); + } + + private void testUpload(RestOperations template, String cseUrlPrefix) throws IOException { + String file1Content = "Hello World"; + File file1 = File.createTempFile("jaxrstest1", ".txt"); + FileUtils.writeStringToFile(file1, file1Content, StandardCharsets.UTF_8, false); + + testFileUpload(template, cseUrlPrefix, file1, file1Content); + testFileAndStringUpload(template, cseUrlPrefix, file1, file1Content); + } + + private void testFileUpload(RestOperations template, String cseUrlPrefix, File file1, String file1Content) + throws IOException { + String result1 = template.postForObject(cseUrlPrefix + "/upload1", new HttpEntity<>(new HashMap<>()), String.class); + TestMgr.check("null file", result1); + + Map map = new HashMap<>(); + map.put("file1", new FileSystemResource(file1)); + String file2Content = "Hello EveryOne"; + File file2 = File.createTempFile("测试2", ".txt"); + FileUtils.writeStringToFile(file2, file2Content, StandardCharsets.UTF_8, false); + map.put("file2", new FileSystemResource(file2)); + String expect = String.format("%s:%s:%s\n" + "%s:%s:%s", + file1.getName(), + MediaType.TEXT_PLAIN, + file1Content, + file2.getName(), + MediaType.TEXT_PLAIN, + file2Content); + String result2 = template.postForObject(cseUrlPrefix + "/upload1", new HttpEntity<>(map), String.class); + TestMgr.check(expect, result2); + } + + private void testFileAndStringUpload(RestOperations template, String cseUrlPrefix, File file1, String file1Content) { + Map map = new HashMap<>(); + String message = "hi"; + map.put("file1", new FileSystemResource(file1)); + map.put("message", message); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA); + String expect = String.format("%s:%s:%s:%s", + file1.getName(), + MediaType.TEXT_PLAIN, + file1Content, + message); + String result = template.postForObject(cseUrlPrefix + "/upload2", new HttpEntity<>(map, headers), String.class); + TestMgr.check(expect, result); + } +} diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFormRequestSchema.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFormRequestSchema.java index f0e9938c120..591302058de 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFormRequestSchema.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFormRequestSchema.java @@ -29,7 +29,7 @@ import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component @@ -37,7 +37,7 @@ public class TestFormRequestSchema implements CategorizedTestCase { private static final Logger LOGGER = LoggerFactory.getLogger(TestFormRequestSchema.class); - private RestTemplate restTemplate = RestTemplateBuilder.create(); + private final RestOperations restTemplate = RestTemplateBuilder.create(); @Override public void testRestTransport() throws Exception { @@ -45,19 +45,16 @@ public void testRestTransport() throws Exception { // testFormRequestFail会关闭连接,防止下个测试用例失败,睡眠2s Thread.sleep(2000); testFormRequestSuccess(); + testFormRequestBufferSize(); } // formSize is less than default maxFormAttributeSize , success - private void testFormRequestSuccess() throws Exception { + private void testFormRequestSuccess() { try { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap formData = new LinkedMultiValueMap<>(); - StringBuilder stringBuffer = new StringBuilder(); - for (int i = 0; i < 512; i++) { - stringBuffer.append("a"); - } - formData.add("formData", stringBuffer.toString()); + formData.add("formData", "a".repeat(512)); HttpEntity> requestEntity = new HttpEntity<>(formData, headers); ResponseEntity responseEntity = restTemplate .postForEntity("cse://jaxrs/form/formRequest", requestEntity, String.class); @@ -69,15 +66,11 @@ private void testFormRequestSuccess() throws Exception { } // formSize is greater than default maxFormAttributeSize , throw exception - private void testFormRequestFail() throws Exception { + private void testFormRequestFail() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap formData = new LinkedMultiValueMap<>(); - StringBuilder stringBuffer = new StringBuilder(); - for (int i = 0; i < 1688; i++) { - stringBuffer.append("a"); - } - formData.add("formData", String.valueOf(stringBuffer)); + formData.add("formData", "a".repeat(1688)); HttpEntity> requestEntity = new HttpEntity<>(formData, headers); try { restTemplate.postForEntity("cse://jaxrs/form/formRequest", requestEntity, String.class); @@ -86,4 +79,26 @@ private void testFormRequestFail() throws Exception { TestMgr.check(e.getMessage().contains("Internal Server Error"), true); } } + + private void testFormRequestBufferSize() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap formData = new LinkedMultiValueMap<>(); + // we can not test a situation for form exceed max buffer size, because the netty buffer is very + // big and the trunc can always be decoded and cached buffer size is always 0. + formData.add("F0123456789001234567890012345678900123456789001234567890" + + "0123456789001234567890012345678900123456789001234567890", "a".repeat(1020) + // we can not test a situation for form exceed max buffer size, because the netty buffer is very + // big and the trunc can always be decoded and cached buffer size is always 0. + ); + HttpEntity> requestEntity = new HttpEntity<>(formData, headers); + try { + ResponseEntity responseEntity = + restTemplate.postForEntity("cse://jaxrs/form/formLongName", requestEntity, String.class); + TestMgr.check(responseEntity.getBody(), "formRequest success : 1020"); + } catch (Throwable e) { + LOGGER.error("testFormRequestBufferSize-->", e); + TestMgr.failed("", e); + } + } } diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestQueryParamSchema.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestQueryParamSchema.java index dccffc20e07..8c0933172a6 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestQueryParamSchema.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestQueryParamSchema.java @@ -22,7 +22,7 @@ import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class TestQueryParamSchema implements CategorizedTestCase { @@ -30,7 +30,7 @@ public interface QueryParamService { String testQueryEncode(String param); } - private final RestTemplate restTemplate = RestTemplateBuilder.create(); + private final RestOperations restTemplate = RestTemplateBuilder.create(); @RpcReference(microserviceName = "jaxrs", schemaId = "QueryParamSchema") private QueryParamService queryParamService; diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestQueryParamWithListSchema.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestQueryParamWithListSchema.java index 40c9b8d0ebf..19aa1c66ee8 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestQueryParamWithListSchema.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestQueryParamWithListSchema.java @@ -21,18 +21,17 @@ import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class TestQueryParamWithListSchema implements CategorizedTestCase { - private RestTemplate restTemplate = RestTemplateBuilder.create(); + private RestOperations restTemplate = RestTemplateBuilder.create(); @Override public void testAllTransport() throws Exception { testMulti(); testCSV(); testSSV(); - testTSV(); testPipes(); } @@ -41,7 +40,6 @@ public void testRestTransport() throws Exception { testMultiRest(); testCSVRest(); testSSVRest(); - testTSVRest(); testPipesRest(); } @@ -51,7 +49,6 @@ public void testHighwayTransport() throws Exception { testMultiHighway(); testCSVHighway(); testSSVHighway(); - testTSVHighway(); testPipesHighway(); } @@ -66,32 +63,19 @@ private void testCSVRest() { } private void testSSV() { - TestMgr.check("2:[1, 2]", + TestMgr.check("1:[1%202]", restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList=1%202", String.class)); - TestMgr.check("2:[, ]", + TestMgr.check("1:[%20]", restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList=%20", String.class)); TestMgr.check("1:[]", restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList=", String.class)); - } - - private void testTSVHighway() { - TestMgr.check("null", - restTemplate.getForObject("cse://jaxrs/queryList/queryListTSV?", String.class)); - } - private void testTSVRest() { - TestMgr.check("0:[]", - restTemplate.getForObject("cse://jaxrs/queryList/queryListTSV?", String.class)); - } - - private void testTSV() { TestMgr.check("2:[1, 2]", - restTemplate - .getForObject("cse://jaxrs/queryList/queryListTSV?queryList={1}", String.class, "1\t2")); + restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList={1}", String.class, "1 2")); TestMgr.check("2:[, ]", - restTemplate.getForObject("cse://jaxrs/queryList/queryListTSV?queryList={1}", String.class, "\t")); + restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList={1}", String.class, " ")); TestMgr.check("1:[]", - restTemplate.getForObject("cse://jaxrs/queryList/queryListTSV?queryList=", String.class)); + restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList=", String.class)); } private void testPipesHighway() { diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestReactiveSchema.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestReactiveSchema.java index 09d257087eb..6126c053ad3 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestReactiveSchema.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestReactiveSchema.java @@ -24,19 +24,21 @@ import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class TestReactiveSchema implements CategorizedTestCase { @SuppressWarnings("rawtypes") public void testRestTransport() throws Exception { - RestTemplate restTemplate = RestTemplateBuilder.create(); + RestOperations restTemplate = RestTemplateBuilder.create(); try { restTemplate.getForObject("cse://jaxrs/reactive/testSyncInvokeInEventLoop?a=1&b=2", int.class); TestMgr.check(true, false); } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), 500); - TestMgr.check(((Map) e.getErrorData()).get("message"), "Can not execute sync logic in event loop."); + TestMgr.check(((Map) e.getErrorData()).get("message"), + "Unexpected exception when processing jaxrs.ReactiveSchema.testSyncInvokeInEventLoop. " + + "Can not execute sync logic in event loop."); } } } diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamRestTemplateClient.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamRestTemplateClient.java index 33e8d9f5a70..decb4a4207f 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamRestTemplateClient.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamRestTemplateClient.java @@ -33,11 +33,11 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class BeanParamRestTemplateClient implements CategorizedTestCase { - RestTemplate restTemplate; + RestOperations restTemplate; public BeanParamRestTemplateClient() { restTemplate = RestTemplateBuilder.create(); @@ -77,9 +77,12 @@ private void testBeanParam() { } private void testUpload() { - BufferedInputStream bufferedInputStream0 = new BufferedInputStream(new ByteArrayInputStream("up0".getBytes(StandardCharsets.UTF_8))); - BufferedInputStream bufferedInputStream1 = new BufferedInputStream(new ByteArrayInputStream("up1".getBytes(StandardCharsets.UTF_8))); - BufferedInputStream bufferedInputStream2 = new BufferedInputStream(new ByteArrayInputStream("up2".getBytes(StandardCharsets.UTF_8))); + BufferedInputStream bufferedInputStream0 = new BufferedInputStream( + new ByteArrayInputStream("up0".getBytes(StandardCharsets.UTF_8))); + BufferedInputStream bufferedInputStream1 = new BufferedInputStream( + new ByteArrayInputStream("up1".getBytes(StandardCharsets.UTF_8))); + BufferedInputStream bufferedInputStream2 = new BufferedInputStream( + new ByteArrayInputStream("up2".getBytes(StandardCharsets.UTF_8))); HashMap formData = new HashMap<>(); formData.put("up0", bufferedInputStream0); diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/injectBean/TestInjectBeanSchema.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/injectBean/TestInjectBeanSchema.java index 175701c233d..02d1ee1d97e 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/injectBean/TestInjectBeanSchema.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/injectBean/TestInjectBeanSchema.java @@ -21,11 +21,11 @@ import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class TestInjectBeanSchema implements CategorizedTestCase { - RestTemplate restTemplate = RestTemplateBuilder.create(); + RestOperations restTemplate = RestTemplateBuilder.create(); @Override public void testAllTransport() throws Exception { diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/pojoDefault/DefaultModelServiceClient.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/pojoDefault/DefaultModelServiceClient.java index 99744957e67..a221853b6a8 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/pojoDefault/DefaultModelServiceClient.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/pojoDefault/DefaultModelServiceClient.java @@ -17,20 +17,20 @@ package org.apache.servicecomb.demo.jaxrs.client.pojoDefault; +import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.jaxrs.server.pojoDefault.DefaultResponseModel; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; public class DefaultModelServiceClient { - private static RestTemplate template = RestTemplateBuilder.create(); + private static RestOperations template = RestTemplateBuilder.create(); private static String urlPrefix = "cse://jaxrs/DefaultModelService"; public static void run() { // highway do not support this feature - ArchaiusUtils.setProperty("servicecomb.references.transport.jaxrs", "rest"); + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport.jaxrs", "rest"); testDefaultModelService(); } diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/validation/ValidationServiceClient.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/validation/ValidationServiceClient.java index 803a1dfe450..4b99fff0359 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/validation/ValidationServiceClient.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/validation/ValidationServiceClient.java @@ -19,24 +19,25 @@ import java.util.ArrayList; -import javax.ws.rs.core.Response.Status; - +import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.jaxrs.server.validation.ValidationModel; import org.apache.servicecomb.demo.validator.Teacher; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; + +import jakarta.ws.rs.core.Response.Status; public class ValidationServiceClient { - private static RestTemplate template = RestTemplateBuilder.create(); + private static RestOperations template = RestTemplateBuilder.create(); private static String urlPrefix = "cse://jaxrs/ValidationService"; public static void run() { // highway do not support this feature - ArchaiusUtils.setProperty("servicecomb.references.transport.jaxrs", "rest"); + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport.jaxrs", "rest"); testValidation(); } @@ -89,7 +90,7 @@ private static void testValidation() { } catch (InvocationException e) { TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); - TestMgr.check(e.getErrorData().toString().contains("Parameter is required."), true); + TestMgr.check(((CommonExceptionData) e.getErrorData()).getMessage().contains("Parameter is not valid"), true); } Teacher teacher = new Teacher(); diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultResponseModel.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultResponseModel.java index 6c4350a07f4..ea5c2072ff5 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultResponseModel.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultResponseModel.java @@ -17,9 +17,9 @@ package org.apache.servicecomb.demo.jaxrs.server.pojoDefault; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.Null; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Null; public class DefaultResponseModel { @Min(20) diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/resources/log4j2.xml b/demo/demo-jaxrs/jaxrs-client/src/main/resources/log4j2.xml index 6e6586771bb..c51f7ad5036 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/resources/log4j2.xml +++ b/demo/demo-jaxrs/jaxrs-client/src/main/resources/log4j2.xml @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/resources/microservice.yaml b/demo/demo-jaxrs/jaxrs-client/src/main/resources/microservice.yaml index 050476b83bf..07493f07ef8 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/resources/microservice.yaml +++ b/demo/demo-jaxrs/jaxrs-client/src/main/resources/microservice.yaml @@ -20,8 +20,10 @@ servicecomb: application: jaxrstest name: jaxrsClient version: 0.0.1 - registry: + registry: + sc: address: http://127.0.0.1:30100 + enableSwaggerRegistration: true matchGroup: instanceIsolationTest: | @@ -55,6 +57,16 @@ servicecomb: check: enabled: false + # use service center to find schema info + openAPI: + registry: + # enable service center OpenAPI registry, and need set enableSwaggerRegistration: true + registry: + enabled: true + # disable instance OpenAPI registry + instance: + enabled: false + # test configurations. you can choose any implementation. default using local. # using nacos configuration @@ -79,4 +91,4 @@ jaxrstest: low-2.a.high-1.b: 3 low-2.a.high-2.b: 4 -# test configurations. \ No newline at end of file +# test configurations. diff --git a/demo/demo-jaxrs/jaxrs-server/pom.xml b/demo/demo-jaxrs/jaxrs-server/pom.xml index d5f54551ec9..cc80ad1521c 100644 --- a/demo/demo-jaxrs/jaxrs-server/pom.xml +++ b/demo/demo-jaxrs/jaxrs-server/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-jaxrs - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT jaxrs-server Java Chassis::Demo::JAXRS::Server @@ -32,19 +32,15 @@ org.apache.servicecomb.demo demo-schema - - org.apache.servicecomb - provider-jaxrs - - - org.apache.servicecomb - provider-springmvc - org.glassfish.jersey.core jersey-client + + org.glassfish.jersey.core + jersey-common + org.apache.servicecomb swagger-invocation-validator @@ -65,28 +61,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsServer.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsServer.java index 6948444a8bd..4cd136d0fbc 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsServer.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsServer.java @@ -20,13 +20,11 @@ import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.demo.RestObjectMapperWithStringMapper; import org.apache.servicecomb.demo.RestObjectMapperWithStringMapperNotWriteNull; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class JaxrsServer { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(JaxrsServer.class).web(WebApplicationType.NONE).run(args); diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java index 81e350f7b35..8626f86087a 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java @@ -19,68 +19,66 @@ import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.Part; -import javax.ws.rs.Consumes; -import javax.ws.rs.CookieParam; -import javax.ws.rs.DELETE; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response.Status; - import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.demo.ignore.InputModelForTestIgnore; import org.apache.servicecomb.demo.ignore.OutputModelForTestIgnore; -import org.apache.servicecomb.demo.jaxbbean.JAXBPerson; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.foundation.common.part.FilePart; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; -import org.apache.servicecomb.swagger.extend.annotations.ResponseHeaders; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ResponseHeader; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.vertx.core.json.JsonObject; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.CookieParam; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response.Status; @RestSchema(schemaId = "codeFirst") @Path("/codeFirstJaxrs") @Produces(MediaType.APPLICATION_JSON) public class CodeFirstJaxrs { - @ApiResponse(code = 200, response = User.class, message = "") - @ResponseHeaders({@ResponseHeader(name = "h1", response = String.class), - @ResponseHeader(name = "h2", response = String.class)}) + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = User.class)), description = "", + headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), + @Header(name = "h2", schema = @Schema(implementation = String.class))}) @Path("/cseResponse") @GET public Response cseResponse(InvocationContext c1) { Response response = Response.createSuccess(Status.ACCEPTED, new User()); - response.setHeader("h1", "h1v " + c1.getContext().get(Const.SRC_MICROSERVICE)); + response.setHeader("h1", "h1v " + c1.getContext().get(CoreConst.SRC_MICROSERVICE)); InvocationContext c2 = ContextUtils.getInvocationContext(); - response.setHeader("h2", "h2v " + c2.getContext().get(Const.SRC_MICROSERVICE)); + response.setHeader("h2", "h2v " + c2.getContext().get(CoreConst.SRC_MICROSERVICE)); return response; } @@ -98,14 +96,6 @@ public String textPlain(String body) { return body; } - @Path("/appXml") - @POST - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_XML) - public JAXBPerson appXml(JAXBPerson body) { - return body; - } - @Path("/bytes") @POST public byte[] bytes(byte[] input) { @@ -132,7 +122,7 @@ public int add(@FormParam("a") int a, @FormParam("b") int b) { @Path("/reduce") @GET - @ApiImplicitParams({@ApiImplicitParam(name = "a", dataType = "integer", format = "int32", paramType = "query")}) + @Parameters({@Parameter(name = "a", schema = @Schema(implementation = int.class), in = ParameterIn.QUERY)}) public int reduce(HttpServletRequest request, @CookieParam("b") int b) { int a = Integer.parseInt(request.getParameter("a")); return a - b; @@ -223,50 +213,16 @@ public String testRawJsonAnnotation(@RawJsonRequestBody String jsonInput) { @Path("/traceId") @GET public String getTraceId() { - return ContextUtils.getInvocationContext().getContext(Const.TRACE_ID_NAME); - } - - @Path("/upload1") - @POST - @Produces(MediaType.TEXT_PLAIN) - public String fileUpload1(@FormParam("file1") Part file1, @FormParam("file2") Part file2) throws IOException { - if (file1 == null || file2 == null) { - return "null file"; - } - try (InputStream is1 = file1.getInputStream(); InputStream is2 = file2.getInputStream()) { - String content1 = IOUtils.toString(is1, StandardCharsets.UTF_8); - String content2 = IOUtils.toString(is2, StandardCharsets.UTF_8); - return String.format("%s:%s:%s\n" + "%s:%s:%s", - file1.getSubmittedFileName(), - file1.getContentType(), - content1, - file2.getSubmittedFileName(), - file2.getContentType(), - content2); - } + return ContextUtils.getInvocationContext().getContext(CoreConst.TRACE_ID_NAME); } @GET @Path("/responseLong") - @ApiResponse(code = 200, response = Object.class, message = "") + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Object.class)), description = "") public Response responseLong() { return Response.createSuccess(Long.MAX_VALUE); } - @Path("/upload2") - @POST - @Produces(MediaType.TEXT_PLAIN) - public String fileUpload2(@FormParam("file1") Part file1, @FormParam("message") String message) throws IOException { - try (InputStream is1 = file1.getInputStream()) { - String content1 = IOUtils.toString(is1, StandardCharsets.UTF_8); - return String.format("%s:%s:%s:%s", - file1.getSubmittedFileName(), - file1.getContentType(), - content1, - message); - } - } - @Path("/download/testDeleteAfterFinished") @GET public Part testDeleteAfterFinished(@QueryParam("name") String name, @QueryParam("content") String content) diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/ComputeImpl.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/ComputeImpl.java index af5a2774d5a..e5735de44fe 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/ComputeImpl.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/ComputeImpl.java @@ -20,20 +20,6 @@ import java.nio.charset.StandardCharsets; import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; - import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.provider.rest.common.RestSchema; @@ -41,6 +27,24 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "compute") @Path("/compute") @Produces(MediaType.APPLICATION_JSON) @@ -55,6 +59,8 @@ public int add(@FormParam("a") int a, @FormParam("b") int b) { @Path("/reduce") @GET + @Parameters(value = {@Parameter(in = ParameterIn.QUERY, name = "a", schema = @Schema(implementation = int.class)), + @Parameter(in = ParameterIn.QUERY, name = "b", schema = @Schema(implementation = int.class))}) public int reduce(@Context HttpServletRequest request) { int a = Integer.parseInt(request.getParameter("a")); int b = Integer.parseInt(request.getParameter("b")); @@ -74,7 +80,8 @@ public Person sayHello(Person user) { public String testRawJsonString(String jsonInput) { Map person; try { - person = RestObjectMapperFactory.getRestObjectMapper().readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); + person = RestObjectMapperFactory.getRestObjectMapper() + .readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); } catch (Exception e) { e.printStackTrace(); return null; diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FileUploadSchema.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FileUploadSchema.java new file mode 100644 index 00000000000..dfaac37fa2d --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FileUploadSchema.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.jaxrs.server; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +import jakarta.servlet.http.Part; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +/** + * Test and illustrate JaxRS uploads. + * + * Use @FormParam to annotate a multipart/form-data item. + */ +@RestSchema(schemaId = "FileUploadSchema") +@Path("/fileUpload") +public class FileUploadSchema { + @Path("/upload1") + @POST + @Produces(MediaType.TEXT_PLAIN) + public String fileUpload1(@FormParam("file1") Part file1, @FormParam("file2") Part file2) throws IOException { + if (file1 == null || file2 == null) { + return "null file"; + } + try (InputStream is1 = file1.getInputStream(); InputStream is2 = file2.getInputStream()) { + String content1 = IOUtils.toString(is1, StandardCharsets.UTF_8); + String content2 = IOUtils.toString(is2, StandardCharsets.UTF_8); + return String.format("%s:%s:%s\n" + "%s:%s:%s", + file1.getSubmittedFileName(), + file1.getContentType(), + content1, + file2.getSubmittedFileName(), + file2.getContentType(), + content2); + } + } + + @Path("/upload2") + @POST + @Produces(MediaType.TEXT_PLAIN) + public String fileUpload2(@FormParam("file1") Part file1, @FormParam("message") String message) throws IOException { + try (InputStream is1 = file1.getInputStream()) { + String content1 = IOUtils.toString(is1, StandardCharsets.UTF_8); + return String.format("%s:%s:%s:%s", + file1.getSubmittedFileName(), + file1.getContentType(), + content1, + message); + } + } +} diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FormRequestSchema.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FormRequestSchema.java index 7ceaf74be04..2670681217a 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FormRequestSchema.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FormRequestSchema.java @@ -17,15 +17,15 @@ package org.apache.servicecomb.demo.jaxrs.server; -import javax.ws.rs.Consumes; -import javax.ws.rs.FormParam; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; - import org.apache.servicecomb.provider.rest.common.RestSchema; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "FormRequestSchema") @Path("/form") @Produces(MediaType.APPLICATION_JSON) @@ -38,4 +38,11 @@ public String formRequestSuccess(@FormParam("formData") String formData) throws return "formRequest success : " + formData.length(); } + @Path("/formLongName") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public String formLongName(@FormParam("F0123456789001234567890012345678900123456789001234567890" + + "0123456789001234567890012345678900123456789001234567890") String formData) throws Exception { + return "formRequest success : " + formData.length(); + } } diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/JaxRSDefaultValues.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/JaxRSDefaultValues.java index 5a2788e648b..17769d3c291 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/JaxRSDefaultValues.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/JaxRSDefaultValues.java @@ -16,21 +16,21 @@ */ package org.apache.servicecomb.demo.jaxrs.server; -import javax.servlet.http.HttpServletRequest; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import javax.ws.rs.Consumes; -import javax.ws.rs.DefaultValue; -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 jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DefaultValue; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.provider.rest.common.RestSchema; diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/JaxrsDemoHttpServerFilter.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/JaxrsDemoHttpServerFilter.java deleted file mode 100644 index 726a3aeeda8..00000000000 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/JaxrsDemoHttpServerFilter.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.servicecomb.demo.jaxrs.server; - -import java.util.concurrent.CompletableFuture; - -import org.apache.servicecomb.common.rest.filter.HttpServerFilter; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; -import org.apache.servicecomb.swagger.invocation.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class JaxrsDemoHttpServerFilter implements HttpServerFilter { - private static final Logger LOGGER = LoggerFactory.getLogger(JaxrsDemoHttpServerFilter.class); - - @Override - public int getOrder() { - return 0; - } - - @Override - public Response afterReceiveRequest(Invocation invocation, HttpServletRequestEx requestEx) { - return null; - } - - @Override - public CompletableFuture beforeSendResponseAsync(Invocation invocation, HttpServletResponseEx responseEx) { - // in 404 situation, invocation is null and a NPE is thrown - LOGGER.info("JaxrsDemoHttpServerFilter is called, operation=[{}]", invocation.getOperationName()); - return CompletableFuture.completedFuture(null); - } -} diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/QueryParamSchema.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/QueryParamSchema.java index a15ce1b34bf..80985d68541 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/QueryParamSchema.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/QueryParamSchema.java @@ -17,9 +17,9 @@ package org.apache.servicecomb.demo.jaxrs.server; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; import org.apache.servicecomb.provider.rest.common.RestSchema; diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/QueryParamWithListSchema.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/QueryParamWithListSchema.java index d63b313f60b..d15849423ac 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/QueryParamWithListSchema.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/QueryParamWithListSchema.java @@ -19,44 +19,48 @@ import java.util.List; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; - import org.apache.servicecomb.provider.rest.common.RestSchema; -import io.swagger.annotations.ApiParam; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.Explode; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.enums.ParameterStyle; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; @RestSchema(schemaId = "QueryParamWithListSchema") @Path("/queryList") public class QueryParamWithListSchema { @Path("queryListCSV") @GET - public String queryListCSV(@ApiParam(collectionFormat = "csv") @QueryParam("queryList") List queryList) { + public String queryListCSV( + @Parameter(name = "queryList", in = ParameterIn.QUERY, style = ParameterStyle.FORM, explode = Explode.FALSE) + @QueryParam("queryList") List queryList) { return queryList == null ? "null" : queryList.size() + ":" + queryList; } @Path("queryListSSV") @GET - public String queryListSSV(@ApiParam(collectionFormat = "ssv") @QueryParam("queryList") List queryList) { - return queryList == null ? "null" : queryList.size() + ":" + queryList; - } - - @Path("queryListTSV") - @GET - public String queryListTSV(@ApiParam(collectionFormat = "tsv") @QueryParam("queryList") List queryList) { + public String queryListSSV( + @Parameter(name = "queryList", in = ParameterIn.QUERY, style = ParameterStyle.SPACEDELIMITED, explode = Explode.FALSE) + @QueryParam("queryList") List queryList) { return queryList == null ? "null" : queryList.size() + ":" + queryList; } @Path("queryListPIPES") @GET - public String queryListPIPES(@ApiParam(collectionFormat = "pipes") @QueryParam("queryList") List queryList) { + public String queryListPIPES( + @Parameter(name = "queryList", in = ParameterIn.QUERY, style = ParameterStyle.PIPEDELIMITED, explode = Explode.FALSE) + @QueryParam("queryList") List queryList) { return queryList == null ? "null" : queryList.size() + ":" + queryList; } @Path("queryListMULTI") @GET - public String queryListMULTI(@ApiParam(collectionFormat = "multi") @QueryParam("queryList") List queryList) { + public String queryListMULTI( + @Parameter(name = "queryList", in = ParameterIn.QUERY, style = ParameterStyle.FORM, explode = Explode.TRUE) + @QueryParam("queryList") List queryList) { return queryList == null ? "null" : queryList.size() + ":" + queryList; } } diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/ReactiveSchema.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/ReactiveSchema.java index 4087198aab0..2143d27a04c 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/ReactiveSchema.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/ReactiveSchema.java @@ -17,15 +17,15 @@ package org.apache.servicecomb.demo.jaxrs.server; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; - import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "ReactiveSchema") @Path("/reactive") @@ -35,7 +35,7 @@ public class ReactiveSchema { @Path("/testSyncInvokeInEventLoop") @GET public int testSyncInvokeInEventLoop(@QueryParam("a") int a, @QueryParam("b") int b) { - RestTemplate client = RestTemplateBuilder.create(); + RestOperations client = RestTemplateBuilder.create(); return client.getForObject("cse://jaxrs/compute/reduce?a=1&b=2", int.class); } } diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/RequestClientTimeOut.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/RequestClientTimeOut.java index 387165123d6..2f17c2fc6f8 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/RequestClientTimeOut.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/RequestClientTimeOut.java @@ -17,11 +17,11 @@ package org.apache.servicecomb.demo.jaxrs.server; -import javax.ws.rs.FormParam; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.demo.validator.Student; import org.apache.servicecomb.provider.rest.common.RestSchema; diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/SchemeInterfaceJaxrs.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/SchemeInterfaceJaxrs.java index 8546d2c25eb..c9c9b311535 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/SchemeInterfaceJaxrs.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/SchemeInterfaceJaxrs.java @@ -17,11 +17,11 @@ package org.apache.servicecomb.demo.jaxrs.server; -import javax.validation.constraints.Min; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.validation.constraints.Min; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.RequestParam; diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/Validator.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/Validator.java index 9f5bef60ae6..a5c5f9b8332 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/Validator.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/Validator.java @@ -17,15 +17,15 @@ package org.apache.servicecomb.demo.jaxrs.server; -import javax.validation.Valid; -import javax.validation.constraints.Min; -import javax.ws.rs.FormParam; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Min; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.demo.validator.Student; import org.apache.servicecomb.provider.rest.common.RestSchema; diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/BeanParamTestService.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/BeanParamTestService.java index 1e2a040bc62..8f037cff595 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/BeanParamTestService.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/BeanParamTestService.java @@ -20,15 +20,15 @@ import java.io.IOException; import java.util.Scanner; -import javax.servlet.http.Part; -import javax.ws.rs.BeanParam; -import javax.ws.rs.Consumes; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.BeanParam; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/TestBeanParameter.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/TestBeanParameter.java index 489b29c567d..3ccff412a5e 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/TestBeanParameter.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/TestBeanParameter.java @@ -17,11 +17,11 @@ package org.apache.servicecomb.demo.jaxrs.server.beanParam; -import javax.ws.rs.CookieParam; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; +import jakarta.ws.rs.CookieParam; +import jakarta.ws.rs.DefaultValue; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.QueryParam; import com.fasterxml.jackson.annotation.JsonIgnore; diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/TestBeanParameterWithUpload.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/TestBeanParameterWithUpload.java index 80530aba63d..b6e3685ef47 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/TestBeanParameterWithUpload.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/TestBeanParameterWithUpload.java @@ -17,9 +17,9 @@ package org.apache.servicecomb.demo.jaxrs.server.beanParam; -import javax.servlet.http.Part; -import javax.ws.rs.FormParam; -import javax.ws.rs.QueryParam; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.QueryParam; public class TestBeanParameterWithUpload { @QueryParam("query") diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBeanSchema.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBeanSchema.java index 14f2ee4c701..59395b02687 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBeanSchema.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBeanSchema.java @@ -19,8 +19,8 @@ import java.util.List; -import javax.ws.rs.GET; -import javax.ws.rs.Path; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.provider.rest.common.RestSchema; diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/MultiErrorCodeService.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/MultiErrorCodeService.java index 7f76e6ac645..9a5c6be64b0 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/MultiErrorCodeService.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/MultiErrorCodeService.java @@ -20,22 +20,22 @@ import java.util.ArrayList; import java.util.List; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.core.Response.Status; - import org.apache.servicecomb.demo.multiErrorCode.MultiRequest; import org.apache.servicecomb.demo.multiErrorCode.MultiResponse200; import org.apache.servicecomb.demo.multiErrorCode.MultiResponse400; import org.apache.servicecomb.demo.multiErrorCode.MultiResponse500; import org.apache.servicecomb.provider.rest.common.RestSchema; -import org.apache.servicecomb.swagger.extend.annotations.ResponseHeaders; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import io.swagger.annotations.ResponseHeader; +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Response.Status; @RestSchema(schemaId = "MultiErrorCodeService") @Path("MultiErrorCodeService") @@ -43,20 +43,20 @@ public class MultiErrorCodeService { @Path("/errorCode") @POST @ApiResponses({ - @ApiResponse(code = 200, response = MultiResponse200.class, message = ""), - @ApiResponse(code = 400, response = MultiResponse400.class, message = ""), - @ApiResponse(code = 500, response = MultiResponse500.class, message = "")}) + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = MultiResponse200.class)), description = ""), + @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(implementation = MultiResponse400.class)), description = ""), + @ApiResponse(responseCode = "500", content = @Content(schema = @Schema(implementation = MultiResponse500.class)), description = "")}) public MultiResponse200 errorCode(MultiRequest request) { if (request.getCode() == 400) { MultiResponse400 r = new MultiResponse400(); r.setCode(400); r.setMessage("bad request"); - throw new InvocationException(javax.ws.rs.core.Response.Status.BAD_REQUEST, r); + throw new InvocationException(jakarta.ws.rs.core.Response.Status.BAD_REQUEST, r); } else if (request.getCode() == 500) { MultiResponse500 r = new MultiResponse500(); r.setCode(500); r.setMessage("internal error"); - throw new InvocationException(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR, r); + throw new InvocationException(jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR, r); } else { MultiResponse200 r = new MultiResponse200(); r.setCode(200); @@ -68,10 +68,15 @@ public MultiResponse200 errorCode(MultiRequest request) { @Path("/errorCodeWithHeader") @POST @ApiResponses({ - @ApiResponse(code = 200, response = MultiResponse200.class, message = ""), - @ApiResponse(code = 400, response = MultiResponse400.class, message = ""), - @ApiResponse(code = 500, response = MultiResponse500.class, message = "")}) - @ResponseHeaders({@ResponseHeader(name = "x-code", response = String.class)}) + @ApiResponse(responseCode = "200", headers = { + @Header(name = "x-code", schema = @Schema(implementation = String.class))}, + content = @Content(schema = @Schema(implementation = MultiResponse200.class)), description = ""), + @ApiResponse(responseCode = "400", headers = { + @Header(name = "x-code", schema = @Schema(implementation = String.class))}, + content = @Content(schema = @Schema(implementation = MultiResponse400.class)), description = ""), + @ApiResponse(responseCode = "500", headers = { + @Header(name = "x-code", schema = @Schema(implementation = String.class))}, + content = @Content(schema = @Schema(implementation = MultiResponse500.class)), description = "")}) public Response errorCodeWithHeader(MultiRequest request) { Response response = new Response(); if (request.getCode() == 400) { @@ -106,19 +111,24 @@ public Response errorCodeWithHeader(MultiRequest request) { @Path("/errorCodeWithHeaderJAXRS") @POST @ApiResponses({ - @ApiResponse(code = 200, response = MultiResponse200.class, message = ""), - @ApiResponse(code = 400, response = MultiResponse400.class, message = ""), - @ApiResponse(code = 500, response = MultiResponse500.class, message = "")}) - @ResponseHeaders({@ResponseHeader(name = "x-code", response = String.class)}) - public javax.ws.rs.core.Response errorCodeWithHeaderJAXRS(MultiRequest request) { - javax.ws.rs.core.Response response; + @ApiResponse(responseCode = "200", headers = { + @Header(name = "x-code", schema = @Schema(implementation = String.class))}, + content = @Content(schema = @Schema(implementation = MultiResponse200.class)), description = ""), + @ApiResponse(responseCode = "400", headers = { + @Header(name = "x-code", schema = @Schema(implementation = String.class))}, + content = @Content(schema = @Schema(implementation = MultiResponse400.class)), description = ""), + @ApiResponse(responseCode = "500", headers = { + @Header(name = "x-code", schema = @Schema(implementation = String.class))}, + content = @Content(schema = @Schema(implementation = MultiResponse500.class)), description = "")}) + public jakarta.ws.rs.core.Response errorCodeWithHeaderJAXRS(MultiRequest request) { + jakarta.ws.rs.core.Response response; if (request.getCode() == 400) { MultiResponse400 r = new MultiResponse400(); r.setCode(request.getCode()); r.setMessage(request.getMessage()); // If got many types for different status code, we can only using InvocationException for failed error code like 400-500. // The result for Failed Family(e.g. 400-500), can not set return value as target type directly or will give exception. - response = javax.ws.rs.core.Response.status(Status.BAD_REQUEST) + response = jakarta.ws.rs.core.Response.status(Status.BAD_REQUEST) .entity(new InvocationException(Status.BAD_REQUEST, r)) .header("x-code", "400") .build(); @@ -126,7 +136,7 @@ public javax.ws.rs.core.Response errorCodeWithHeaderJAXRS(MultiRequest request) MultiResponse500 r = new MultiResponse500(); r.setCode(request.getCode()); r.setMessage(request.getMessage()); - response = javax.ws.rs.core.Response.status(Status.INTERNAL_SERVER_ERROR) + response = jakarta.ws.rs.core.Response.status(Status.INTERNAL_SERVER_ERROR) .entity(new InvocationException(Status.INTERNAL_SERVER_ERROR, r)) .header("x-code", "500") .build(); @@ -135,7 +145,7 @@ public javax.ws.rs.core.Response errorCodeWithHeaderJAXRS(MultiRequest request) r.setCode(request.getCode()); r.setMessage(request.getMessage()); // If error code is OK family(like 200), we can use the target type. - response = javax.ws.rs.core.Response.status(Status.OK) + response = jakarta.ws.rs.core.Response.status(Status.OK) .entity(r) .header("x-code", "200") .build(); @@ -146,7 +156,7 @@ public javax.ws.rs.core.Response errorCodeWithHeaderJAXRS(MultiRequest request) @Path("/noClientErrorCode") @POST @ApiResponses({ - @ApiResponse(code = 400, response = NoClientErrorCode400.class, message = "")}) + @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(implementation = NoClientErrorCode400.class)), description = "")}) public List noClientErrorCode(MultiRequest request) { if (request.getCode() == 400) { NoClientErrorCode400 r = new NoClientErrorCode400(); diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultModelService.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultModelService.java index 7296a0970e1..36020868cbd 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultModelService.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultModelService.java @@ -17,8 +17,8 @@ package org.apache.servicecomb.demo.jaxrs.server.pojoDefault; -import javax.ws.rs.POST; -import javax.ws.rs.Path; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; import org.apache.servicecomb.provider.rest.common.RestSchema; diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultRequestModel.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultRequestModel.java index 0cc535703df..b8053ca8086 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultRequestModel.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultRequestModel.java @@ -17,9 +17,9 @@ package org.apache.servicecomb.demo.jaxrs.server.pojoDefault; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.Null; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Null; public class DefaultRequestModel { @Min(20) diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultResponseModel.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultResponseModel.java index bf4288a2869..9c505699866 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultResponseModel.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultResponseModel.java @@ -17,9 +17,9 @@ package org.apache.servicecomb.demo.jaxrs.server.pojoDefault; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.Null; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Null; public class DefaultResponseModel { @Min(20) diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/validation/ValidationService.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/validation/ValidationService.java index 4b4620375b0..7b18a4f7322 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/validation/ValidationService.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/validation/ValidationService.java @@ -17,13 +17,13 @@ package org.apache.servicecomb.demo.jaxrs.server.validation; -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; import org.apache.servicecomb.demo.validator.Teacher; import org.apache.servicecomb.provider.rest.common.RestSchema; diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter b/demo/demo-jaxrs/jaxrs-server/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter deleted file mode 100644 index 5e737d86d70..00000000000 --- a/demo/demo-jaxrs/jaxrs-server/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.demo.jaxrs.server.JaxrsDemoHttpServerFilter \ No newline at end of file diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher b/demo/demo-jaxrs/jaxrs-server/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher index afff0839503..a3a19c72aa3 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher +++ b/demo/demo-jaxrs/jaxrs-server/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.demo.jaxrs.server.injectBean.InjectBeanVertxHttpDispatcher \ No newline at end of file +org.apache.servicecomb.demo.jaxrs.server.injectBean.InjectBeanVertxHttpDispatcher diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/resources/log4j2.xml b/demo/demo-jaxrs/jaxrs-server/src/main/resources/log4j2.xml index 6e6586771bb..c51f7ad5036 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/resources/log4j2.xml +++ b/demo/demo-jaxrs/jaxrs-server/src/main/resources/log4j2.xml @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/resources/microservice.yaml b/demo/demo-jaxrs/jaxrs-server/src/main/resources/microservice.yaml index e22dfaebf93..475708a55b1 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/resources/microservice.yaml +++ b/demo/demo-jaxrs/jaxrs-server/src/main/resources/microservice.yaml @@ -20,12 +20,15 @@ servicecomb: application: jaxrstest name: jaxrs version: 0.0.2 - registry: + registry: + sc: address: http://127.0.0.1:30100 + enableSwaggerRegistration: true rest: address: 0.0.0.0:8080 server: - maxFormAttributeSize: 1024 + maxFormAttributeSize: 1024 # for testing, and bigger than netty buffer allocator + maxFormBufferedBytes: 100 highway: address: 0.0.0.0:7070 uploads: @@ -40,3 +43,13 @@ servicecomb: timeout: check: enabled: false + + # use service center to find schema info + openAPI: + registry: + # enable service center OpenAPI registry, and need set enableSwaggerRegistration: true + registry: + enabled: true + # disable instance OpenAPI registry + instance: + enabled: false diff --git a/demo/demo-jaxrs/pom.xml b/demo/demo-jaxrs/pom.xml index e5d7900b12a..014baac0318 100644 --- a/demo/demo-jaxrs/pom.xml +++ b/demo/demo-jaxrs/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-jaxrs Java Chassis::Demo::JAXRS @@ -32,6 +32,10 @@ jaxrs-client + + org.apache.servicecomb + solution-basic + org.apache.servicecomb registry-service-center @@ -52,5 +56,13 @@ org.apache.logging.log4j log4j-slf4j-impl + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + diff --git a/demo/demo-local-registry/README.md b/demo/demo-local-registry/README.md index 7ace9bd1d27..a585a17b7ce 100644 --- a/demo/demo-local-registry/README.md +++ b/demo/demo-local-registry/README.md @@ -1,6 +1,5 @@ This demo is an integration test for testing local registration an discovery. -Local registration and discovery is a mechanism that do not using Service Center, -`Microservice` and `MicroserviceInstance` are constructed from configuration file. - +Local registration and discovery is a mechanism that do not using Service Center, +`Microservice` and `MicroserviceInstance` are constructed from configuration file. diff --git a/demo/demo-local-registry/demo-local-registry-client/pom.xml b/demo/demo-local-registry/demo-local-registry-client/pom.xml index 30c855fe57e..352e0cf26aa 100644 --- a/demo/demo-local-registry/demo-local-registry-client/pom.xml +++ b/demo/demo-local-registry/demo-local-registry-client/pom.xml @@ -25,17 +25,13 @@ org.apache.servicecomb.demo demo-local-registry - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT - org.apache.servicecomb.demo.localRegistryClient.Application + org.apache.servicecomb.demo.localRegistryClient.LocalRegistryClientApplication - - org.apache.servicecomb - java-chassis-spring-boot-starter-servlet - org.apache.servicecomb.demo demo-schema @@ -49,24 +45,60 @@ demo-local-registry-server + + + + io.fabric8 + docker-maven-plugin + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + /maven/maven/${demo.service.name}-${project.version}.jar + + + ServiceComb is ready + + + 8080 + + + + + + 8080:8080 + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config-local - ${project.version} - - - - diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/Application.java b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/Application.java deleted file mode 100644 index b43f3695552..00000000000 --- a/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/Application.java +++ /dev/null @@ -1,56 +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.servicecomb.demo.localRegistryClient; - -import org.apache.servicecomb.demo.CategorizedTestCaseRunner; -import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.builder.SpringApplicationBuilder; - -@SpringBootApplication -@EnableServiceComb -public class Application { - public static void main(final String[] args) throws Exception { - new SpringApplicationBuilder().sources(Application.class).web(WebApplicationType.SERVLET).build().run(args); - - registerSchema(); - - runTest(); - - TestMgr.summary(); - if (!TestMgr.errors().isEmpty()) { - throw new IllegalStateException("tests failed"); - } - } - - private static void registerSchema() { - RegistrationManager.INSTANCE.getSwaggerLoader().registerSwagger("demo-local-registry", - "demo-local-registry-server", - "CodeFirstEndpoint", CodeFirstService.class); - RegistrationManager.INSTANCE.getSwaggerLoader().registerSwagger("demo-local-registry", - "demo-local-registry-server-bean", - "CodeFirstEndpoint", CodeFirstService.class); - } - - private static void runTest() throws Exception { - CategorizedTestCaseRunner.runCategorizedTestCase("demo-local-registry-server"); - } -} diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/CodeFirstService.java b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/CodeFirstService.java index cbe329eb9d2..05973bd18de 100644 --- a/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/CodeFirstService.java +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/CodeFirstService.java @@ -17,10 +17,10 @@ package org.apache.servicecomb.demo.localRegistryClient; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; @Path("/register/url/codeFirst") @Produces("application/json") diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryClientApplication.java b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryClientApplication.java new file mode 100644 index 00000000000..76f1f2d570f --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryClientApplication.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.servicecomb.demo.localRegistryClient; + +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.demo.CategorizedTestCaseRunner; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class LocalRegistryClientApplication { + public static void main(final String[] args) throws Exception { + new SpringApplicationBuilder().sources(LocalRegistryClientApplication.class) + .web(WebApplicationType.SERVLET).build() + .run(args); + + registerSchema(); + + runTest(); + + TestMgr.summary(); + if (!TestMgr.errors().isEmpty()) { + throw new IllegalStateException("tests failed"); + } + } + + private static void registerSchema() { + SCBEngine.getInstance().getOpenAPIRegistryManager().registerOpenAPI("demo-local-registry", + "demo-local-registry-server", + "CodeFirstEndpoint", CodeFirstService.class); + SCBEngine.getInstance().getOpenAPIRegistryManager().registerOpenAPI("demo-local-registry", + "demo-local-registry-server-bean", + "CodeFirstEndpoint", CodeFirstService.class); + } + + private static void runTest() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("demo-local-registry-server"); + } +} diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryServerTest.java b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryServerTest.java index c50508cc0b7..06a24601e03 100644 --- a/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryServerTest.java +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryServerTest.java @@ -24,21 +24,43 @@ import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.api.registry.Microservice; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class LocalRegistryServerTest implements CategorizedTestCase { + // demo-local-registry-server-bean use yaml to register service, and register schema by + // local file loading @RpcReference(microserviceName = "demo-local-registry-server", schemaId = "CodeFirstEndpoint") private CodeFirstService codeFirstService; + @RpcReference(microserviceName = "demo-local-registry-server", schemaId = "ServerEndpoint") + private ServerService serverService; + + // demo-local-registry-server-bean use bean to register service, and register schema part by + // local file loading, part by bean class @RpcReference(microserviceName = "demo-local-registry-server-bean", schemaId = "CodeFirstEndpoint") private CodeFirstService codeFirstServiceBean; + @RpcReference(microserviceName = "demo-local-registry-server-bean", schemaId = "ServerEndpoint") + private ServerService serverServiceBean; + + // demo-local-registry-server-bean2 use bean to register service and schema @RpcReference(microserviceName = "demo-local-registry-server-bean2", schemaId = "CodeFirstEndpoint2") private CodeFirstService codeFirstServiceBean2; + @RpcReference(microserviceName = "demo-local-registry-server-bean2", schemaId = "ServerEndpoint") + private ServerService serverServiceBean2; + + private DiscoveryManager discoveryManager; + + @Autowired + public void setDiscoveryManager(DiscoveryManager discoveryManager) { + this.discoveryManager = discoveryManager; + } + @Override public void testRestTransport() throws Exception { testServerGetName(); @@ -47,17 +69,18 @@ public void testRestTransport() throws Exception { } private void testGetAllMicroservice() { - List microserviceList = DiscoveryManager.INSTANCE.getAllMicroservices(); - int expectedCount = 0; - - for (Microservice m : microserviceList) { - if (m.getServiceName().equals("demo-local-registry-client") - || m.getServiceName().equals("demo-local-registry-server") - || m.getServiceName().equals("demo-local-registry-server-bean")) { - expectedCount++; - } - } - TestMgr.check(3, expectedCount); + List microserviceList = discoveryManager + .findServiceInstances("demo-local-registry", "demo-local-registry-client"); + TestMgr.check(1, microserviceList.size()); + microserviceList = discoveryManager + .findServiceInstances("demo-local-registry", "demo-local-registry-server"); + TestMgr.check(1, microserviceList.size()); + microserviceList = discoveryManager + .findServiceInstances("demo-local-registry", "demo-local-registry-server-bean"); + TestMgr.check(1, microserviceList.size()); + microserviceList = discoveryManager + .findServiceInstances("demo-local-registry", "demo-local-registry-server-bean2"); + TestMgr.check(1, microserviceList.size()); } private void testCodeFirstGetName() { @@ -67,12 +90,18 @@ private void testCodeFirstGetName() { } private void testServerGetName() { - RestTemplate template = RestTemplateBuilder.create(); + RestOperations template = RestTemplateBuilder.create(); TestMgr.check("2", template .getForObject("cse://demo-local-registry-server/register/url/prefix/getName?name=2", String.class)); TestMgr.check("2", template .getForObject("cse://demo-local-registry-server-bean/register/url/prefix/getName?name=2", String.class)); + TestMgr.check("2", template + .getForObject("cse://demo-local-registry-server-bean2/register/url/prefix/getName?name=2", + String.class)); + TestMgr.check("2", serverService.getName("2")); + TestMgr.check("2", serverServiceBean.getName("2")); + TestMgr.check("2", serverServiceBean2.getName("2")); } } diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/RegistryBeansConfiguration.java b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/RegistryBeansConfiguration.java index 029d1425745..28c5d3a1f25 100644 --- a/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/RegistryBeansConfiguration.java +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/RegistryBeansConfiguration.java @@ -27,6 +27,7 @@ import org.springframework.context.annotation.Configuration; @Configuration +@SuppressWarnings("unused") public class RegistryBeansConfiguration { @Bean public RegistryBean demoLocalRegistryServerBean() { @@ -41,7 +42,7 @@ public RegistryBean demoLocalRegistryServerBean() { .setVersion("0.0.3") .setAppId("demo-local-registry") .addSchemaId("ServerEndpoint") - .addSchemaId("CodeFirstEndpoint") + .addSchemaInterface("CodeFirstEndpoint", CodeFirstService.class) .setInstances(new Instances().setInstances(instances)); } @@ -57,6 +58,7 @@ public RegistryBean demoLocalRegistryServerBean2() { .setId("003") .setVersion("0.0.3") .setAppId("demo-local-registry") + .addSchemaId("ServerEndpoint") .addSchemaInterface("CodeFirstEndpoint2", CodeFirstService.class) .setInstances(new Instances().setInstances(instances)); } diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/ServerService.java b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/ServerService.java new file mode 100644 index 00000000000..f8211fd9a36 --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/ServerService.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.localRegistryClient; + +public interface ServerService { + String getName(String name); +} diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/application.yml b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/application.yml index a2b1698dcb6..fcf4bb18309 100644 --- a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/application.yml +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/application.yml @@ -18,9 +18,8 @@ server: port: 8082 -# java-chassis configurations - -APPLICATION_ID: demo-local-registry -service_description: - name: demo-local-registry-client - version: 0.0.1 +servicecomb: + service: + application: demo-local-registry + name: demo-local-registry-client + version: 0.0.1 diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean/ServerEndpoint.yaml b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean/ServerEndpoint.yaml index 78c9d1bca63..2adeb24781c 100644 --- a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean/ServerEndpoint.yaml +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean/ServerEndpoint.yaml @@ -15,29 +15,28 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for org.apache.servicecomb.demo.registry.ServerEndpoint" - x-java-interface: "gen.swagger.ServerEndpointIntf" -basePath: "/register/url/prefix" -schemes: -- "http" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.demo.localRegistryServer.ServerEndpoint + version: 1.0.0 +servers: +- url: /register/url/prefix paths: /getName: get: - operationId: "getName" + operationId: getName parameters: - - name: "name" - in: "query" + - name: name + in: query required: true - type: "string" + schema: + type: string responses: "200": - description: "response of 200" - schema: - type: "string" \ No newline at end of file + description: response of 200 + content: + application/json: + schema: + type: string +components: + schemas: {} diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean2/ServerEndpoint.yaml b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean2/ServerEndpoint.yaml new file mode 100644 index 00000000000..2adeb24781c --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean2/ServerEndpoint.yaml @@ -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. +## --------------------------------------------------------------------------- + +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.demo.localRegistryServer.ServerEndpoint + version: 1.0.0 +servers: +- url: /register/url/prefix +paths: + /getName: + get: + operationId: getName + parameters: + - name: name + in: query + required: true + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string +components: + schemas: {} diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server/ServerEndpoint.yaml b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server/ServerEndpoint.yaml index 78c9d1bca63..2adeb24781c 100644 --- a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server/ServerEndpoint.yaml +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server/ServerEndpoint.yaml @@ -15,29 +15,28 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for org.apache.servicecomb.demo.registry.ServerEndpoint" - x-java-interface: "gen.swagger.ServerEndpointIntf" -basePath: "/register/url/prefix" -schemes: -- "http" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.demo.localRegistryServer.ServerEndpoint + version: 1.0.0 +servers: +- url: /register/url/prefix paths: /getName: get: - operationId: "getName" + operationId: getName parameters: - - name: "name" - in: "query" + - name: name + in: query required: true - type: "string" + schema: + type: string responses: "200": - description: "response of 200" - schema: - type: "string" \ No newline at end of file + description: response of 200 + content: + application/json: + schema: + type: string +components: + schemas: {} diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/registry.yaml b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/registry.yaml index 2ad3c23269f..cb5a3bcd4d9 100644 --- a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/registry.yaml +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/registry.yaml @@ -24,4 +24,4 @@ demo-local-registry-server: - CodeFirstEndpoint instances: - endpoints: - - rest://localhost:8080 \ No newline at end of file + - rest://localhost:8080 diff --git a/demo/demo-local-registry/demo-local-registry-client/src/test/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryIT.java b/demo/demo-local-registry/demo-local-registry-client/src/test/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryIT.java index 6157da6d357..f8f12fd9cfa 100644 --- a/demo/demo-local-registry/demo-local-registry-client/src/test/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryIT.java +++ b/demo/demo-local-registry/demo-local-registry-client/src/test/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryIT.java @@ -30,7 +30,7 @@ public void setUp() throws Exception { @Test public void clientGetsNoError() throws Exception { - Application.main(new String[0]); + LocalRegistryClientApplication.main(new String[0]); Assertions.assertTrue(TestMgr.errors().isEmpty()); } diff --git a/demo/demo-local-registry/demo-local-registry-server/pom.xml b/demo/demo-local-registry/demo-local-registry-server/pom.xml index 38a2a6875e3..8832ac2dc8a 100644 --- a/demo/demo-local-registry/demo-local-registry-server/pom.xml +++ b/demo/demo-local-registry/demo-local-registry-server/pom.xml @@ -25,21 +25,17 @@ org.apache.servicecomb.demo demo-local-registry - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT - org.apache.servicecomb.demo.localRegistryServer.ServerApplication + org.apache.servicecomb.demo.localRegistryServer.LocalRegistryServerApplication jakarta.ws.rs jakarta.ws.rs-api - - org.apache.servicecomb - java-chassis-spring-boot-starter-servlet - org.apache.servicecomb.demo demo-schema @@ -59,28 +55,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/CodeFirstEndpoint.java b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/CodeFirstEndpoint.java index 781eed61c81..06fdf6b7939 100644 --- a/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/CodeFirstEndpoint.java +++ b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/CodeFirstEndpoint.java @@ -17,26 +17,21 @@ package org.apache.servicecomb.demo.localRegistryServer; -import javax.ws.rs.core.MediaType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "CodeFirstEndpoint") @RequestMapping(path = "/register/url/codeFirst", produces = MediaType.APPLICATION_JSON) public class CodeFirstEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(CodeFirstEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/LocalRegistryServerApplication.java b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/LocalRegistryServerApplication.java new file mode 100644 index 00000000000..e574177a982 --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/LocalRegistryServerApplication.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.servicecomb.demo.localRegistryServer; + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class LocalRegistryServerApplication { + public static void main(final String[] args) throws Exception { + new SpringApplicationBuilder().sources(LocalRegistryServerApplication.class).web(WebApplicationType.SERVLET).build() + .run(args); + + SelfServiceInvoker invoker = BeanUtils.getBean("SelfServiceInvoker"); + invoker.latch.await(10, TimeUnit.SECONDS); + TestMgr.check(invoker.result, "hello"); + + TestMgr.summary(); + if (!TestMgr.errors().isEmpty()) { + System.exit(1); + } + } +} diff --git a/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/ServerApplication.java b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/ServerApplication.java deleted file mode 100644 index dcd1746ab79..00000000000 --- a/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/ServerApplication.java +++ /dev/null @@ -1,44 +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.servicecomb.demo.localRegistryServer; - -import java.util.concurrent.TimeUnit; - -import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.builder.SpringApplicationBuilder; - -@SpringBootApplication -@EnableServiceComb -public class ServerApplication { - public static void main(final String[] args) throws Exception { - new SpringApplicationBuilder().sources(ServerApplication.class).web(WebApplicationType.SERVLET).build().run(args); - - SelfServiceInvoker invoker = BeanUtils.getBean("SelfServiceInvoker"); - invoker.latch.await(10, TimeUnit.SECONDS); - TestMgr.check(invoker.result, "hello"); - - TestMgr.summary(); - if (!TestMgr.errors().isEmpty()) { - System.exit(1); - } - } -} diff --git a/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/ServerEndpoint.java b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/ServerEndpoint.java index 41a9cd532ff..2db9d87dd05 100644 --- a/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/ServerEndpoint.java +++ b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/ServerEndpoint.java @@ -17,26 +17,21 @@ package org.apache.servicecomb.demo.localRegistryServer; -import javax.ws.rs.core.MediaType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(ServerEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-local-registry/demo-local-registry-server/src/main/resources/application.yml b/demo/demo-local-registry/demo-local-registry-server/src/main/resources/application.yml index 54a66a2d58b..1478383598e 100644 --- a/demo/demo-local-registry/demo-local-registry-server/src/main/resources/application.yml +++ b/demo/demo-local-registry/demo-local-registry-server/src/main/resources/application.yml @@ -19,11 +19,10 @@ server: port: 8080 # java-chassis configurations - -APPLICATION_ID: demo-local-registry -service_description: - name: demo-local-registry-server - version: 0.0.1 servicecomb: + service: + application: demo-local-registry + name: demo-local-registry-server + version: 0.0.1 rest: address: 0.0.0.0:8080 diff --git a/demo/demo-local-registry/pom.xml b/demo/demo-local-registry/pom.xml index eaceca49d25..c33eebf22e7 100644 --- a/demo/demo-local-registry/pom.xml +++ b/demo/demo-local-registry/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-local-registry Java Chassis::Demo::Local Registry @@ -33,6 +33,10 @@ + + org.apache.servicecomb + solution-basic + org.apache.servicecomb java-chassis-spring-boot-starter-servlet diff --git a/demo/demo-multi-registries/README.md b/demo/demo-multi-registries/README.md new file mode 100644 index 00000000000..762bd27eba3 --- /dev/null +++ b/demo/demo-multi-registries/README.md @@ -0,0 +1,4 @@ +# About + +* This demo enables both local-registry and sc-registry. +* This demo using local-registry to call service center api. diff --git a/demo/demo-multi-registries/demo-multi-registries-client/pom.xml b/demo/demo-multi-registries/demo-multi-registries-client/pom.xml index ee6aee2d403..3577950317f 100644 --- a/demo/demo-multi-registries/demo-multi-registries-client/pom.xml +++ b/demo/demo-multi-registries/demo-multi-registries-client/pom.xml @@ -25,17 +25,13 @@ org.apache.servicecomb.demo demo-multi-registries - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT - org.apache.servicecomb.demo.registry.Application + org.apache.servicecomb.demo.registry.MultiRegistriesClientApplication - - org.apache.servicecomb - java-chassis-spring-boot-starter-servlet - org.apache.servicecomb.demo demo-schema @@ -49,24 +45,88 @@ demo-multi-registries-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/Application.java b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/Application.java deleted file mode 100644 index 0395f4267e7..00000000000 --- a/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/Application.java +++ /dev/null @@ -1,47 +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.servicecomb.demo.registry; - -import org.apache.servicecomb.demo.CategorizedTestCaseRunner; -import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.stereotype.Component; - -@SpringBootApplication -@EnableServiceComb -@Component -public class Application { - - public static void main(final String[] args) throws Exception { - new SpringApplicationBuilder().sources(Application.class).web(WebApplicationType.SERVLET).build().run(args); - - runTest(); - } - - public static void runTest() throws Exception { - CategorizedTestCaseRunner.runCategorizedTestCase("demo-multi-registries-server"); - - TestMgr.summary(); - if (!TestMgr.errors().isEmpty()) { - throw new IllegalStateException("tests failed"); - } - } -} diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesClientApplication.java b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesClientApplication.java new file mode 100644 index 00000000000..87efbff570a --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesClientApplication.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 org.apache.servicecomb.demo.registry; + +import org.apache.servicecomb.demo.CategorizedTestCaseRunner; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.stereotype.Component; + +@SpringBootApplication +@Component +public class MultiRegistriesClientApplication { + + public static void main(final String[] args) throws Exception { + new SpringApplicationBuilder().sources(MultiRegistriesClientApplication.class) + .web(WebApplicationType.SERVLET).build().run(args); + + runTest(); + } + + public static void runTest() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("demo-multi-registries-server"); + + TestMgr.summary(); + if (!TestMgr.errors().isEmpty()) { + throw new IllegalStateException("tests failed"); + } + } +} diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesServerTestCase.java b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesServerTestCase.java index 3ca2ef174d9..abbd66a3d1b 100644 --- a/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesServerTestCase.java +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesServerTestCase.java @@ -17,21 +17,27 @@ package org.apache.servicecomb.demo.registry; -import java.util.HashSet; import java.util.List; -import java.util.Set; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.api.registry.Microservice; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class MultiRegistriesServerTestCase implements CategorizedTestCase { - RestTemplate template = RestTemplateBuilder.create(); + RestOperations template = RestTemplateBuilder.create(); + + private DiscoveryManager discoveryManager; + + @Autowired + public void setDiscoveryManager(DiscoveryManager discoveryManager) { + this.discoveryManager = discoveryManager; + } @Override public void testRestTransport() throws Exception { @@ -40,18 +46,18 @@ public void testRestTransport() throws Exception { } private void testGetAllMicroservice() { - List microserviceList = DiscoveryManager.INSTANCE.getAllMicroservices(); - Set names = new HashSet<>(); - - for (Microservice m : microserviceList) { - if (m.getServiceName().equals("demo-multi-registries-client") - || m.getServiceName().equals("demo-multi-registries-server") - || m.getServiceName().equals("thirdParty-service-center") - || m.getServiceName().equals("thirdParty-no-schema-server")) { - names.add(m.getServiceName()); - } - } - TestMgr.check(4, names.size()); + List microserviceList = discoveryManager + .findServiceInstances("demo-multi-registries", "demo-multi-registries-client"); + TestMgr.check(2, microserviceList.size()); // both local and sc has one. + microserviceList = discoveryManager + .findServiceInstances("demo-multi-registries", "demo-multi-registries-server"); + TestMgr.check(1, microserviceList.size()); + microserviceList = discoveryManager + .findServiceInstances("demo-multi-registries", "thirdParty-service-center"); + TestMgr.check(1, microserviceList.size()); + microserviceList = discoveryManager + .findServiceInstances("demo-multi-registries", "thirdParty-no-schema-server"); + TestMgr.check(1, microserviceList.size()); } private void testServerGetName() { diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/SchemaDiscoveryTestCase.java b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/SchemaDiscoveryTestCase.java index 3ed4c1a60e1..0bf58aa7c3f 100644 --- a/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/SchemaDiscoveryTestCase.java +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/SchemaDiscoveryTestCase.java @@ -18,19 +18,18 @@ package org.apache.servicecomb.demo.registry; import org.apache.servicecomb.demo.CategorizedTestCase; -import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class SchemaDiscoveryTestCase implements CategorizedTestCase { - @RpcReference(microserviceName = "thirdParty-no-schema-server", schemaId = "ServerEndpoint") - IServerEndpoint serverEndpoint; + // Do not support this feature any more since 3.0.0 +// @RpcReference(microserviceName = "thirdParty-no-schema-server", schemaId = "ServerEndpoint") +// IServerEndpoint serverEndpoint; @Override public void testRestTransport() throws Exception { // invoke thirdParty-no-schema-server(mocked by demo-multi-registries-server) - TestMgr.check("hello", serverEndpoint.getName("hello")); + // TestMgr.check("hello", serverEndpoint.getName("hello")); } @Override diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/ServiceCenterEndpoint.java b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/ServiceCenterEndpoint.java index ceba6f4309c..491732f92bc 100644 --- a/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/ServiceCenterEndpoint.java +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/ServiceCenterEndpoint.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.demo.registry; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/application.yml b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/application.yml index 38c3cbee55b..e440ff6c897 100644 --- a/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/application.yml +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/application.yml @@ -18,13 +18,18 @@ server: port: 8082 -# java-chassis configurations - -APPLICATION_ID: demo-multi-registries -service_description: - name: demo-multi-registries-client - version: 0.0.1 servicecomb: service: - registry: + application: demo-multi-registries + name: demo-multi-registries-client + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 + demo-multi-registries: + thirdParty-service-center: + enabled: false + local: + demo-multi-registries: + demo-multi-registries-server: + enabled: false diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/microservices/thirdParty-service-center/ServiceCenterEndpoint.yaml b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/microservices/thirdParty-service-center/ServiceCenterEndpoint.yaml index 8ba87fadd56..88332e3259a 100644 --- a/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/microservices/thirdParty-service-center/ServiceCenterEndpoint.yaml +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/microservices/thirdParty-service-center/ServiceCenterEndpoint.yaml @@ -3,57 +3,60 @@ ## 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 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, +## 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. ## --------------------------------------------------------------------------- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for org.apache.servicecomb.demo.registry.ServiceCenterEndpoint" - x-java-interface: "gen.swagger.ServiceCenterEndpointIntf" -basePath: "/v4/default/registry" -schemes: - - "http" -consumes: - - "application/json" -produces: - - "application/json" + title: swagger definition for org.apache.servicecomb.demo.localRegistryServer.ServerEndpoint + version: 1.0.0 +servers: + - url: /v4/default/registry paths: /instances: get: - operationId: "getInstances" + operationId: getInstances parameters: - - name: "appId" - in: "query" + - name: appId + in: query required: true - type: "string" - - name: "serviceName" - in: "query" + schema: + type: string + - name: serviceName + in: query required: true - type: "string" - - name: "global" - in: "query" + schema: + type: string + - name: global + in: query required: true - type: "string" - - name: "version" - in: "query" + schema: + type: string + - name: version + in: query required: true - type: "string" - - name: "x-domain-name" - in: "header" + schema: + type: string + - name: x-domain-name + in: header required: true - type: "string" - responses: - "200": - description: "response of 200" schema: - type: "object" \ No newline at end of file + type: string + responses: + 200: + description: response of 200 + content: + application/json: + schema: + type: object +components: + schemas: {} diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/registry.yaml b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/registry.yaml index 3bc2c1e4438..9d39b1b8bd1 100644 --- a/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/registry.yaml +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/registry.yaml @@ -29,7 +29,7 @@ thirdParty-no-schema-server: version: "4.0.0" appid: demo-multi-registries schemaIds: - - ServerEndpoint # not in local , discovery from demo-multi-registries-server + - ServerEndpoint # not in local , discovery from demo-multi-registries-server. Not supported since 3.0.0 instances: - endpoints: - - rest://localhost:8080 # actually will invoke to demo-multi-registries-server \ No newline at end of file + - rest://localhost:8080 # actually will invoke to demo-multi-registries-server diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/test/java/org/apache/servicecomb/demo/registry/MultiRegistriesIT.java b/demo/demo-multi-registries/demo-multi-registries-client/src/test/java/org/apache/servicecomb/demo/registry/MultiRegistriesIT.java index 87588ddea63..1c716783c2d 100644 --- a/demo/demo-multi-registries/demo-multi-registries-client/src/test/java/org/apache/servicecomb/demo/registry/MultiRegistriesIT.java +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/test/java/org/apache/servicecomb/demo/registry/MultiRegistriesIT.java @@ -30,7 +30,7 @@ public void setUp() throws Exception { @Test public void clientGetsNoError() throws Exception { - Application.main(new String[0]); + MultiRegistriesClientApplication.main(new String[0]); Assertions.assertTrue(TestMgr.errors().isEmpty()); } diff --git a/demo/demo-multi-registries/demo-multi-registries-server/pom.xml b/demo/demo-multi-registries/demo-multi-registries-server/pom.xml index bc3978dfb07..4a0b37f4433 100644 --- a/demo/demo-multi-registries/demo-multi-registries-server/pom.xml +++ b/demo/demo-multi-registries/demo-multi-registries-server/pom.xml @@ -25,21 +25,17 @@ org.apache.servicecomb.demo demo-multi-registries - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT - org.apache.servicecomb.demo.registry.ServerApplication + org.apache.servicecomb.demo.registry.MultiRegistriesServerApplication jakarta.ws.rs jakarta.ws.rs-api - - org.apache.servicecomb - java-chassis-spring-boot-starter-servlet - org.apache.servicecomb.demo demo-schema @@ -59,28 +55,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesServerApplication.java b/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesServerApplication.java new file mode 100644 index 00000000000..afacef1155c --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesServerApplication.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.servicecomb.demo.registry; + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class MultiRegistriesServerApplication { + public static void main(final String[] args) throws Exception { + new SpringApplicationBuilder().sources(MultiRegistriesServerApplication.class) + .web(WebApplicationType.SERVLET).build().run(args); + + SelfServiceInvoker invoker = BeanUtils.getBean("SelfServiceInvoker"); + invoker.latch.await(10, TimeUnit.SECONDS); + TestMgr.check(invoker.result, "hello"); + + TestMgr.summary(); + if (!TestMgr.errors().isEmpty()) { + System.exit(1); + } + } +} diff --git a/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/ServerApplication.java b/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/ServerApplication.java deleted file mode 100644 index 2b95ea9e39d..00000000000 --- a/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/ServerApplication.java +++ /dev/null @@ -1,44 +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.servicecomb.demo.registry; - -import java.util.concurrent.TimeUnit; - -import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.builder.SpringApplicationBuilder; - -@SpringBootApplication -@EnableServiceComb -public class ServerApplication { - public static void main(final String[] args) throws Exception { - new SpringApplicationBuilder().sources(ServerApplication.class).web(WebApplicationType.SERVLET).build().run(args); - - SelfServiceInvoker invoker = BeanUtils.getBean("SelfServiceInvoker"); - invoker.latch.await(10, TimeUnit.SECONDS); - TestMgr.check(invoker.result, "hello"); - - TestMgr.summary(); - if (!TestMgr.errors().isEmpty()) { - System.exit(1); - } - } -} diff --git a/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/ServerEndpoint.java b/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/ServerEndpoint.java index 595997f99e1..eb594aaba57 100644 --- a/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/ServerEndpoint.java +++ b/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/ServerEndpoint.java @@ -17,26 +17,21 @@ package org.apache.servicecomb.demo.registry; -import javax.ws.rs.core.MediaType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(ServerEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-multi-registries/demo-multi-registries-server/src/main/resources/application.yml b/demo/demo-multi-registries/demo-multi-registries-server/src/main/resources/application.yml index a616b13bdb8..1104cebdbd7 100644 --- a/demo/demo-multi-registries/demo-multi-registries-server/src/main/resources/application.yml +++ b/demo/demo-multi-registries/demo-multi-registries-server/src/main/resources/application.yml @@ -18,15 +18,13 @@ server: port: 8080 -# java-chassis configurations - -APPLICATION_ID: demo-multi-registries -service_description: - name: demo-multi-registries-server - version: 0.0.2 servicecomb: service: - registry: + application: demo-multi-registries + name: demo-multi-registries-server + version: 0.0.2 + registry: + sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 diff --git a/demo/demo-multi-registries/pom.xml b/demo/demo-multi-registries/pom.xml index 47ae83e5a00..13f34c9c7af 100644 --- a/demo/demo-multi-registries/pom.xml +++ b/demo/demo-multi-registries/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-multi-registries Java Chassis::Demo::Multi Registry @@ -35,15 +35,15 @@ org.apache.servicecomb - java-chassis-spring-boot-starter-servlet + solution-basic org.apache.servicecomb - registry-local + java-chassis-spring-boot-starter-servlet org.apache.servicecomb - registry-schema-discovery + registry-local org.apache.servicecomb diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/pom.xml b/demo/demo-multi-service-center/demo-multi-service-center-client/pom.xml index 41535613049..2669f28a874 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-client/pom.xml +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-multi-service-center - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -33,10 +33,6 @@ - - org.apache.servicecomb - java-chassis-spring-boot-starter-servlet - org.apache.servicecomb service-center-client @@ -106,8 +102,7 @@ alias - -Dservicecomb.service.registry.address=http://service30100:30100 - -Dservicecomb.service.publishAddress=${docker.hostname} + -Dservicecomb.registry.sc.address=http://service30100:30100 /maven/maven/demo-multi-service-center-servera-${project.version}.jar @@ -115,7 +110,7 @@ service30100:service30100 - Register microservice instance success + ServiceComb is ready 8080 @@ -135,8 +130,7 @@ alias - -Dservicecomb.service.registry.address=http://service40100:30100 - -Dservicecomb.service.publishAddress=${docker.hostname} + -Dservicecomb.registry.sc.address=http://service40100:30100 /maven/maven/demo-multi-service-center-serverb-${project.version}.jar @@ -144,7 +138,7 @@ service40100:service40100 - Register microservice instance success + ServiceComb is ready 8082 @@ -183,46 +177,6 @@ io.fabric8 docker-maven-plugin - - org.codehaus.gmavenplus - gmavenplus-plugin - - - add-default-properties - initialize - - execute - - - - - - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - http://${docker.hostname}:30100 - - - ${jacoco.failsafe.argLine} - - - - - integration-test - verify - - - - diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/Application.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/Application.java index 687e2ffd35a..4264651164e 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/Application.java +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/Application.java @@ -23,14 +23,12 @@ import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.stereotype.Component; @SpringBootApplication -@EnableServiceComb @Component public class Application { @@ -43,7 +41,6 @@ public static void main(final String[] args) throws Exception { public static void runTest() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("demo-multi-service-center-serverA"); - testRegistryThreads(); testTransportThreads(); TestMgr.summary(); if (!TestMgr.errors().isEmpty()) { @@ -71,35 +68,4 @@ private static void testTransportThreads() throws Exception { // transport-vert.x-internal-blocking-1 TestMgr.check(6, expectedThread.size()); } - - private static void testRegistryThreads() throws Exception { - Set threadSet = Thread.getAllStackTraces().keySet(); - List expectedThread = new ArrayList<>(); - threadSet.forEach(thread -> { - if (thread.getName().contains("registry-")) { - expectedThread.add(thread.getName()); - } - }); - //registry-watch-vert.x-eventloop-thread-3(option) - //registry-watch-vert.x-eventloop-thread-2(option) - //registry-watch-vert.x-eventloop-thread-1 - //registry-watch-vert.x-eventloop-thread-0 - //registry-watch-serverB-vert.x-eventloop-thread-3(option) - //registry-watch-serverB-vert.x-eventloop-thread-2(option) - //registry-watch-serverB-vert.x-eventloop-thread-1 - //registry-watch-serverB-vert.x-eventloop-thread-0 - //registry-watch-pool-worker-service-center-watch-1 - //registry-watch-pool-worker-service-center-watch-0 - //registry-watch-serverB-pool-worker-service-center-watch-1 - //registry-watch-serverB-pool-worker-service-center-watch-0 - //registry-vert.x-eventloop-thread-3(option) - //registry-vert.x-eventloop-thread-2(option) - //registry-vert.x-eventloop-thread-1 - //registry-vert.x-eventloop-thread-0 - //registry-serverB-vert.x-eventloop-thread-3(option) - //registry-serverB-vert.x-eventloop-thread-2(option) - //registry-serverB-vert.x-eventloop-thread-1 - //registry-serverB-vert.x-eventloop-thread-0 - TestMgr.check(expectedThread.size() >= 12, expectedThread.size() <= 20); - } } diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java index 85f041034cf..3b11e0582d8 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java @@ -42,6 +42,8 @@ import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; import org.apache.servicecomb.service.center.client.model.SchemaInfo; import org.apache.servicecomb.service.center.client.model.ServiceCenterConfiguration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import com.google.common.base.Charsets; @@ -58,14 +60,21 @@ public class RegistryClientTest implements CategorizedTestCase { // auto test only tests 'hasRegistered=false', can run this client many times to test 'hasRegistered=true' private boolean hasRegistered = true; + private final Environment environment; + + @Autowired + public RegistryClientTest(Environment environment) { + this.environment = environment; + } + @Override public void testRestTransport() throws Exception { - ServiceCenterAddressManager addressManager = new ServiceCenterAddressManager("default", Arrays.asList("http://127.0.0.1:30100"), - new EventBus()); + ServiceCenterAddressManager addressManager = new ServiceCenterAddressManager("default", + Arrays.asList("http://127.0.0.1:30100"), new EventBus(), "", ""); SSLProperties sslProperties = new SSLProperties(); sslProperties.setEnabled(false); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(addressManager, sslProperties, - new DefaultRequestAuthHeaderProvider(), "default", null); + new DefaultRequestAuthHeaderProvider(), "default", null, environment); EventBus eventBus = new SimpleEventBus(); ServiceCenterConfiguration serviceCenterConfiguration = new ServiceCenterConfiguration(); ServiceCenterRegistration serviceCenterRegistration = new ServiceCenterRegistration(serviceCenterClient, diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Configuration.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Configuration.java new file mode 100644 index 00000000000..db7fc11df06 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Configuration.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.multiServiceCenterClient; + +import java.util.List; + +import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; +import org.apache.servicecomb.registry.sc.SCClientUtils; +import org.apache.servicecomb.registry.sc.SCConfigurationProperties; +import org.apache.servicecomb.registry.sc.SCConst; +import org.apache.servicecomb.service.center.client.ServiceCenterClient; +import org.apache.servicecomb.service.center.client.ServiceCenterWatch; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +@Configuration +public class SC2Configuration { + @Bean + @ConfigurationProperties(prefix = SCConst.SC_REGISTRY_PREFIX + "2") + public SCConfigurationProperties scConfigurationProperties2() { + return new SCConfigurationProperties(); + } + + @Bean + public ServiceCenterWatch serviceCenterWatch2( + @Qualifier("scConfigurationProperties2") SCConfigurationProperties scConfigurationProperties2, + List authHeaderProviders, Environment environment) { + return SCClientUtils.serviceCenterWatch(scConfigurationProperties2, authHeaderProviders, environment); + } + + @Bean + public ServiceCenterClient serviceCenterClient2( + @Qualifier("scConfigurationProperties2") SCConfigurationProperties scConfigurationProperties2, + Environment environment) { + return SCClientUtils.serviceCenterClient(scConfigurationProperties2, environment); + } + + @Bean + public SC2Discovery sc2Discovery( + @Qualifier("scConfigurationProperties2") SCConfigurationProperties scConfigurationProperties2, + @Qualifier("serviceCenterClient2") ServiceCenterClient serviceCenterClient2) { + return new SC2Discovery(scConfigurationProperties2, serviceCenterClient2); + } + + @Bean + public SC2Registration sc2Registration( + @Qualifier("scConfigurationProperties2") SCConfigurationProperties scConfigurationProperties2, + @Qualifier("serviceCenterClient2") ServiceCenterClient serviceCenterClient2, + @Qualifier("serviceCenterWatch2") ServiceCenterWatch serviceCenterWatch2) { + return new SC2Registration(scConfigurationProperties2, serviceCenterClient2, serviceCenterWatch2); + } +} diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Discovery.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Discovery.java new file mode 100644 index 00000000000..e6669d294ea --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Discovery.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.servicecomb.demo.multiServiceCenterClient; + +import org.apache.servicecomb.registry.sc.SCConfigurationProperties; +import org.apache.servicecomb.registry.sc.SCConst; +import org.apache.servicecomb.registry.sc.SCDiscovery; +import org.apache.servicecomb.service.center.client.ServiceCenterClient; + +public class SC2Discovery extends SCDiscovery { + public SC2Discovery(SCConfigurationProperties configurationProperties, + ServiceCenterClient serviceCenterClient) { + super(configurationProperties, serviceCenterClient); + } + + @Override + public String name() { + return SCConst.SC_REGISTRY_NAME + "2"; + } +} diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Registration.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Registration.java new file mode 100644 index 00000000000..7fd22f03616 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Registration.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.servicecomb.demo.multiServiceCenterClient; + +import org.apache.servicecomb.registry.sc.SCConfigurationProperties; +import org.apache.servicecomb.registry.sc.SCConst; +import org.apache.servicecomb.registry.sc.SCRegistration; +import org.apache.servicecomb.service.center.client.ServiceCenterClient; +import org.apache.servicecomb.service.center.client.ServiceCenterWatch; + +public class SC2Registration extends SCRegistration { + public SC2Registration(SCConfigurationProperties configurationProperties, + ServiceCenterClient serviceCenterClient, ServiceCenterWatch serviceCenterWatch) { + super(configurationProperties, serviceCenterClient, serviceCenterWatch); + } + + @Override + public String name() { + return SCConst.SC_REGISTRY_NAME + 2; + } +} diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerBRegistryHttpClientOptionsSPI.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerBRegistryHttpClientOptionsSPI.java deleted file mode 100644 index 003a9624e9a..00000000000 --- a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerBRegistryHttpClientOptionsSPI.java +++ /dev/null @@ -1,39 +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.servicecomb.demo.multiServiceCenterClient; - -import org.apache.servicecomb.serviceregistry.client.http.RegistryHttpClientOptionsSPI; - -public class ServerBRegistryHttpClientOptionsSPI extends RegistryHttpClientOptionsSPI { - public static final String CLIENT_NAME = "registry-serverB"; - - @Override - public String clientName() { - return CLIENT_NAME; - } - - @Override - public String getConfigTag() { - return "sc.serverB.consumer"; - } - - @Override - public boolean isSsl() { - return false; - } -} diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerBRegistryWatchHttpClientOptionsSPI.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerBRegistryWatchHttpClientOptionsSPI.java deleted file mode 100644 index 5c25d0194e6..00000000000 --- a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerBRegistryWatchHttpClientOptionsSPI.java +++ /dev/null @@ -1,66 +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.servicecomb.demo.multiServiceCenterClient; - -import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig; - -import com.netflix.config.DynamicPropertyFactory; - -public class ServerBRegistryWatchHttpClientOptionsSPI extends ServerBRegistryHttpClientOptionsSPI { - public static final String CLIENT_NAME = "registry-watch-serverB"; - - private ServiceRegistryConfig serviceRegistryConfig = ServiceRegistryConfig.INSTANCE; - - @Override - public String clientName() { - return CLIENT_NAME; - } - - @Override - public int getOrder() { - // low priority than registry - return super.getOrder() + 1; - } - - @Override - public boolean enabled() { - return serviceRegistryConfig.isWatch(); - } - - @Override - public boolean isWorker() { - return true; - } - - - @Override - public String getWorkerPoolName() { - return "pool-worker-service-center-watch-serverB"; - } - - @Override - public int getWorkerPoolSize() { - return DynamicPropertyFactory.getInstance() - .getIntProperty(ServiceRegistryConfig.WORKER_POOL_SIZE, 2).get(); - } - - @Override - public boolean isProxyEnable() { - return false; - } -} diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerBServiceCenterConfiguration.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerBServiceCenterConfiguration.java deleted file mode 100644 index d27e9676751..00000000000 --- a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerBServiceCenterConfiguration.java +++ /dev/null @@ -1,38 +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.servicecomb.demo.multiServiceCenterClient; - -import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfigCustomizer; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class ServerBServiceCenterConfiguration { - @Bean("serverBServiceCenterConfig") - public ServiceRegistryConfig serverBServiceCenterConfig() { - ServiceRegistryConfig config = ServiceRegistryConfig.buildFromConfiguration(); - return ServiceRegistryConfigCustomizer.from(config) - .addressListFromConfiguration("servicecomb.service.registry-serverB.address") - // use a different http client instance - .setClientName("registry-serverB") - .setWatchClientName("registry-watch-serverB") - .setRegistryName("server-B") - .get(); - } -} diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI deleted file mode 100644 index ea85cbefeb7..00000000000 --- a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI +++ /dev/null @@ -1,19 +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. -# - -org.apache.servicecomb.demo.multiServiceCenterClient.ServerBRegistryHttpClientOptionsSPI -org.apache.servicecomb.demo.multiServiceCenterClient.ServerBRegistryWatchHttpClientOptionsSPI \ No newline at end of file diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/application.yml b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/application.yml index cc3c6f49f44..6daa76e8dee 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/application.yml +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/application.yml @@ -15,18 +15,17 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -# java-chassis configurations -APPLICATION_ID: demo-multi-service-center -service_description: - name: demo-multi-service-center-client - version: 0.0.2 servicecomb: service: - registry: + application: demo-multi-service-center + name: demo-multi-service-center-client + version: 0.0.2 + registry: + sc: address: http://127.0.0.1:30100 - registry-serverB: + sc2: address: http://127.0.0.1:40100 transport: eventloop: - size: 6 \ No newline at end of file + size: 6 diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverA/pom.xml b/demo/demo-multi-service-center/demo-multi-service-center-serverA/pom.xml index 4ee29fd8ba4..ddfd3570a43 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverA/pom.xml +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverA/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-multi-service-center - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -36,10 +36,6 @@ jakarta.ws.rs jakarta.ws.rs-api - - org.apache.servicecomb - java-chassis-spring-boot-starter-servlet - org.apache.servicecomb.demo demo-schema @@ -59,28 +55,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerApplication.java b/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerApplication.java index cb98f8acefd..4515d6c7575 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerApplication.java +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerApplication.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.demo.multiServiceCenter; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class ServerApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(ServerApplication.class) diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerEndpoint.java b/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerEndpoint.java index 2e6efa642b5..16fdb41bdde 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerEndpoint.java +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerEndpoint.java @@ -17,26 +17,21 @@ package org.apache.servicecomb.demo.multiServiceCenter; -import javax.ws.rs.core.MediaType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(ServerEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/resources/application.yml b/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/resources/application.yml index 9c602f434d9..03f54c0db15 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/resources/application.yml +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/resources/application.yml @@ -18,15 +18,13 @@ server: port: 8080 -# java-chassis configurations - -APPLICATION_ID: demo-multi-service-center -service_description: - name: demo-multi-service-center-serverA - version: 0.0.2 servicecomb: service: - registry: + application: demo-multi-service-center + name: demo-multi-service-center-serverA + version: 0.0.2 + registry: + sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverB/pom.xml b/demo/demo-multi-service-center/demo-multi-service-center-serverB/pom.xml index 3d54c86f17a..66dfd58d9b4 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverB/pom.xml +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-multi-service-center - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -36,10 +36,6 @@ jakarta.ws.rs jakarta.ws.rs-api - - org.apache.servicecomb - java-chassis-spring-boot-starter-servlet - org.apache.servicecomb.demo demo-schema @@ -59,28 +55,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ConfigurationEndpoint.java b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ConfigurationEndpoint.java index 89dfebfaf3a..edbb116ccca 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ConfigurationEndpoint.java +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ConfigurationEndpoint.java @@ -19,8 +19,6 @@ import java.util.List; -import javax.ws.rs.core.MediaType; - import org.apache.servicecomb.provider.rest.common.RestSchema; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,7 +29,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import com.netflix.config.DynamicPropertyFactory; +import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "ConfigurationEndpoint") @RequestMapping(path = "/register/url/config", produces = MediaType.APPLICATION_JSON) @@ -72,7 +70,7 @@ public String getValue(@RequestParam(name = "key") String key, @RequestParam(nam if (type == 1) { return environment.getProperty(key); } else if (type == 2) { - return DynamicPropertyFactory.getInstance().getStringProperty(key, null).get(); + return environment.getProperty(key); } else { switch (key) { case "demo.multi.service.center.serverB.key1": diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerApplication.java b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerApplication.java index 851432d7b6f..49106b7b8dd 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerApplication.java +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerApplication.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.demo.multiServiceCenterServerB; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class ServerApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(ServerApplication.class) diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerEndpoint.java b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerEndpoint.java index af822e86853..d14c1d4a7b0 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerEndpoint.java +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerEndpoint.java @@ -17,26 +17,21 @@ package org.apache.servicecomb.demo.multiServiceCenterServerB; -import javax.ws.rs.core.MediaType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(ServerEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/application.yml b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/application.yml index 8303ad5c5e9..e9850bc8c23 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/application.yml +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/application.yml @@ -18,15 +18,13 @@ server: port: 8082 -# java-chassis configurations - -APPLICATION_ID: demo-multi-service-center -service_description: - name: demo-multi-service-center-serverB - version: 0.0.2 servicecomb: service: - registry: + application: demo-multi-service-center + name: demo-multi-service-center-serverB + version: 0.0.2 + registry: + sc: address: http://127.0.0.1:40100 rest: address: 0.0.0.0:8082 @@ -35,4 +33,3 @@ demo.multi.service.center.serverB: key1: key1-boot key4: key4-boot key5: key5 - diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/microservice.yaml b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/microservice.yaml index 52cd6c8eab1..2164cf6ac9f 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/microservice.yaml +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/microservice.yaml @@ -26,4 +26,4 @@ demo.multi.service.center.serverB: key6: ${demo.multi.service.center.serverB.actual.key6} key7: - ${demo.multi.service.center.serverB.actual.key7.1} - - ${demo.multi.service.center.serverB.actual.key7.2} \ No newline at end of file + - ${demo.multi.service.center.serverB.actual.key7.2} diff --git a/demo/demo-multi-service-center/pom.xml b/demo/demo-multi-service-center/pom.xml index 80421137ac1..cf9c101e496 100644 --- a/demo/demo-multi-service-center/pom.xml +++ b/demo/demo-multi-service-center/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-multi-service-center Java Chassis::Demo::Multi Service Centers @@ -34,6 +34,10 @@ + + org.apache.servicecomb + solution-basic + org.apache.servicecomb java-chassis-spring-boot-starter-servlet diff --git a/demo/demo-multiple/a-client/pom.xml b/demo/demo-multiple/a-client/pom.xml index dd0dd2d5009..84131a7909c 100644 --- a/demo/demo-multiple/a-client/pom.xml +++ b/demo/demo-multiple/a-client/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-multiple - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT a-client Java Chassis::Demo::Multiple::A client diff --git a/demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AClientMain.java b/demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AClientMain.java index f00fe545947..907df46c2c7 100644 --- a/demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AClientMain.java +++ b/demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AClientMain.java @@ -19,11 +19,9 @@ import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; -@EnableServiceComb public class AClientMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(AClientMain.class).web(WebApplicationType.NONE).run(args); diff --git a/demo/demo-multiple/a-client/src/main/resources/microservice.yaml b/demo/demo-multiple/a-client/src/main/resources/microservice.yaml index 9a8ea2d7fe7..81a782bafc2 100644 --- a/demo/demo-multiple/a-client/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/a-client/src/main/resources/microservice.yaml @@ -16,11 +16,12 @@ ## --------------------------------------------------------------------------- a-server.name: a-server -APPLICATION_ID: multiple -service_description: - name: a-client - version: 0.0.1 + servicecomb: service: - registry: + application: multiple + name: a-client + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 diff --git a/demo/demo-multiple/a-server/pom.xml b/demo/demo-multiple/a-server/pom.xml index b11afad4c21..d38b12d8d62 100644 --- a/demo/demo-multiple/a-server/pom.xml +++ b/demo/demo-multiple/a-server/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-multiple - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT a-server Java Chassis::Demo::Multiple::A server diff --git a/demo/demo-multiple/a-server/src/main/java/org/apache/servicecomb/demo/multiple/a/server/AServerMain.java b/demo/demo-multiple/a-server/src/main/java/org/apache/servicecomb/demo/multiple/a/server/AServerMain.java index df21aeb54f2..40c96981c7a 100644 --- a/demo/demo-multiple/a-server/src/main/java/org/apache/servicecomb/demo/multiple/a/server/AServerMain.java +++ b/demo/demo-multiple/a-server/src/main/java/org/apache/servicecomb/demo/multiple/a/server/AServerMain.java @@ -17,11 +17,9 @@ package org.apache.servicecomb.demo.multiple.a.server; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; -@EnableServiceComb public class AServerMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(AServerMain.class).web(WebApplicationType.NONE).run(args); diff --git a/demo/demo-multiple/a-server/src/main/resources/microservice.yaml b/demo/demo-multiple/a-server/src/main/resources/microservice.yaml index b24d620d4bb..d916c594f79 100644 --- a/demo/demo-multiple/a-server/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/a-server/src/main/resources/microservice.yaml @@ -15,13 +15,13 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: multiple -service_description: - name: a-server - version: 0.0.1 servicecomb: service: - registry: + application: multiple + name: a-server + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 diff --git a/demo/demo-multiple/b-client/pom.xml b/demo/demo-multiple/b-client/pom.xml index 00386e0ee37..4ab02a66f1b 100644 --- a/demo/demo-multiple/b-client/pom.xml +++ b/demo/demo-multiple/b-client/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-multiple - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT b-client Java Chassis::Demo::Multiple::B client diff --git a/demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BClientMain.java b/demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BClientMain.java index ca10b9fc814..a6400d91a4e 100644 --- a/demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BClientMain.java +++ b/demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BClientMain.java @@ -19,11 +19,9 @@ import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; -@EnableServiceComb public class BClientMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(BClientMain.class).web(WebApplicationType.NONE).run(args); diff --git a/demo/demo-multiple/b-client/src/main/resources/microservice.yaml b/demo/demo-multiple/b-client/src/main/resources/microservice.yaml index ea9f8a88776..8445ed3f4d8 100644 --- a/demo/demo-multiple/b-client/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/b-client/src/main/resources/microservice.yaml @@ -16,11 +16,12 @@ ## --------------------------------------------------------------------------- b-server.name: b-server -APPLICATION_ID: multiple -service_description: - name: b-client - version: 0.0.1 + servicecomb: service: - registry: + application: multiple + name: b-client + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 diff --git a/demo/demo-multiple/b-server/pom.xml b/demo/demo-multiple/b-server/pom.xml index 7b02ee27808..e33e750d8e4 100644 --- a/demo/demo-multiple/b-server/pom.xml +++ b/demo/demo-multiple/b-server/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-multiple - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT b-server diff --git a/demo/demo-multiple/b-server/src/main/java/org/apache/servicecomb/demo/multiple/b/server/BServerMain.java b/demo/demo-multiple/b-server/src/main/java/org/apache/servicecomb/demo/multiple/b/server/BServerMain.java index d3fe61682bb..267925aceae 100644 --- a/demo/demo-multiple/b-server/src/main/java/org/apache/servicecomb/demo/multiple/b/server/BServerMain.java +++ b/demo/demo-multiple/b-server/src/main/java/org/apache/servicecomb/demo/multiple/b/server/BServerMain.java @@ -17,11 +17,9 @@ package org.apache.servicecomb.demo.multiple.b.server; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; -@EnableServiceComb public class BServerMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(BServerMain.class).web(WebApplicationType.NONE).run(args); diff --git a/demo/demo-multiple/b-server/src/main/resources/microservice.yaml b/demo/demo-multiple/b-server/src/main/resources/microservice.yaml index 814dbfae3b2..695ed8f50a8 100644 --- a/demo/demo-multiple/b-server/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/b-server/src/main/resources/microservice.yaml @@ -15,13 +15,13 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: multiple -service_description: - name: b-server - version: 0.0.1 servicecomb: service: - registry: + application: multiple + name: b-server + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 diff --git a/demo/demo-multiple/multiple-client/pom.xml b/demo/demo-multiple/multiple-client/pom.xml index bd6902e0ae9..7dcbb6f14f2 100644 --- a/demo/demo-multiple/multiple-client/pom.xml +++ b/demo/demo-multiple/multiple-client/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-multiple - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT multiple-client Java Chassis::Demo::Multiple::Client @@ -40,7 +40,7 @@ org.apache.servicecomb.demo.multiple.client.MultipleClient - + docker @@ -48,24 +48,88 @@ multiple-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-multiple/multiple-client/src/main/java/org/apache/servicecomb/demo/multiple/client/MultipleClient.java b/demo/demo-multiple/multiple-client/src/main/java/org/apache/servicecomb/demo/multiple/client/MultipleClient.java index 2768bf306d8..ae804ad8bea 100644 --- a/demo/demo-multiple/multiple-client/src/main/java/org/apache/servicecomb/demo/multiple/client/MultipleClient.java +++ b/demo/demo-multiple/multiple-client/src/main/java/org/apache/servicecomb/demo/multiple/client/MultipleClient.java @@ -20,14 +20,12 @@ import org.apache.servicecomb.demo.multiple.a.client.AClient; import org.apache.servicecomb.demo.multiple.b.client.BClient; import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication -@EnableServiceComb @ComponentScan(basePackages = {"org.apache.servicecomb.demo.multiple.a.client", "org.apache.servicecomb.demo.multiple.b.client"}) public class MultipleClient { diff --git a/demo/demo-multiple/multiple-client/src/main/resources/microservice.yaml b/demo/demo-multiple/multiple-client/src/main/resources/microservice.yaml index f06dd95db94..1ece43eac98 100644 --- a/demo/demo-multiple/multiple-client/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/multiple-client/src/main/resources/microservice.yaml @@ -18,7 +18,9 @@ servicecomb-config-order: 1000 a-server.name: merged-server b-server.name: merged-server -APPLICATION_ID: multiple -service_description: - name: merged-client - version: 0.0.1 + +servicecomb: + service: + application: multiple + name: merged-client + version: 0.0.1 diff --git a/demo/demo-multiple/multiple-server/pom.xml b/demo/demo-multiple/multiple-server/pom.xml index 62293637cea..18c7ae19069 100644 --- a/demo/demo-multiple/multiple-server/pom.xml +++ b/demo/demo-multiple/multiple-server/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-multiple - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT multiple-server Java Chassis::Demo::Multiple::Server @@ -54,28 +54,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-multiple/multiple-server/src/main/java/org/apache/servicecomb/demo/multiple/server/MultipleServer.java b/demo/demo-multiple/multiple-server/src/main/java/org/apache/servicecomb/demo/multiple/server/MultipleServer.java index 6e12e828e50..5b830e46e83 100644 --- a/demo/demo-multiple/multiple-server/src/main/java/org/apache/servicecomb/demo/multiple/server/MultipleServer.java +++ b/demo/demo-multiple/multiple-server/src/main/java/org/apache/servicecomb/demo/multiple/server/MultipleServer.java @@ -16,14 +16,12 @@ */ package org.apache.servicecomb.demo.multiple.server; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication -@EnableServiceComb @ComponentScan(basePackages = {"org.apache.servicecomb.demo.multiple.a.server", "org.apache.servicecomb.demo.multiple.b.server"}) public class MultipleServer { diff --git a/demo/demo-multiple/multiple-server/src/main/resources/microservice.yaml b/demo/demo-multiple/multiple-server/src/main/resources/microservice.yaml index 94fb8d2bc30..b1eeade93d7 100644 --- a/demo/demo-multiple/multiple-server/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/multiple-server/src/main/resources/microservice.yaml @@ -16,7 +16,9 @@ ## --------------------------------------------------------------------------- servicecomb-config-order: 1000 -APPLICATION_ID: multiple -service_description: - name: merged-server - version: 0.0.1 + +servicecomb: + service: + application: multiple + name: merged-server + version: 0.0.1 diff --git a/demo/demo-multiple/pom.xml b/demo/demo-multiple/pom.xml index 3b3e1df1392..7496a2050d7 100644 --- a/demo/demo-multiple/pom.xml +++ b/demo/demo-multiple/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-multiple Java Chassis::Demo::Multiple @@ -70,10 +70,6 @@ org.apache.servicecomb.demo demo-schema - - org.apache.servicecomb - provider-pojo - org.apache.servicecomb java-chassis-spring-boot-starter-standalone diff --git a/demo/demo-nacos/README.md b/demo/demo-nacos/README.md new file mode 100644 index 00000000000..9165cac5687 --- /dev/null +++ b/demo/demo-nacos/README.md @@ -0,0 +1,5 @@ +# Notice + +This integration tests is designed for Nacos registry and configuration. And extra test cases include: + +* Test cases related to JAX-RS annotations that demo-jaxrs can not cover. diff --git a/demo/demo-nacos/consumer/pom.xml b/demo/demo-nacos/consumer/pom.xml new file mode 100644 index 00000000000..6dfe326f6a5 --- /dev/null +++ b/demo/demo-nacos/consumer/pom.xml @@ -0,0 +1,104 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-nacos + 3.4.0-SNAPSHOT + + + nacos-consumer + Java Chassis::Demo::NACOS::CONSUMER + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + registry-nacos + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java new file mode 100644 index 00000000000..3e7a2a0e285 --- /dev/null +++ b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ConsumerApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsumerApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java new file mode 100644 index 00000000000..437f88bfafd --- /dev/null +++ b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.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.servicecomb.samples; + +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RestSchema(schemaId = "ConsumerController") +@RequestMapping(path = "/") +public class ConsumerController { + @RpcReference(schemaId = "ProviderController", microserviceName = "provider") + private ProviderService providerService; + + // consumer service which delegate the implementation to provider service. + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { + return providerService.sayHello(name); + } + + @GetMapping("/authIncludePath") + public String authIncludePath() { + return providerService.authIncludePath(); + } +} diff --git a/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java new file mode 100644 index 00000000000..aed6894f941 --- /dev/null +++ b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchema; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchema.class) +public class ConsumerHeaderParamWithListSchema implements IHeaderParamWithListSchema { + @RpcReference(microserviceName = "provider", schemaId = "HeaderParamWithListSchema") + private IHeaderParamWithListSchema provider; + + @Override + public String headerListDefault(List headerList) { + return provider.headerListDefault(headerList); + } + + @Override + public String headerListCSV(List headerList) { + return provider.headerListCSV(headerList); + } + + @Override + public String headerListMULTI(List headerList) { + return provider.headerListMULTI(headerList); + } + + @Override + public String headerListSSV(List headerList) { + return provider.headerListSSV(headerList); + } + + @Override + public String headerListPIPES(List headerList) { + return provider.headerListPIPES(headerList); + } +} diff --git a/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java new file mode 100644 index 00000000000..ee62a110625 --- /dev/null +++ b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +public interface ProviderService { + String sayHello(String name); + + String authIncludePath(); +} diff --git a/demo/demo-nacos/consumer/src/main/resources/application.yml b/demo/demo-nacos/consumer/src/main/resources/application.yml new file mode 100644 index 00000000000..ce6025722a5 --- /dev/null +++ b/demo/demo-nacos/consumer/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-nacos + version: 0.0.1 + name: consumer + registry: + nacos: + serverAddr: ${PAAS_CSE_NACOS_ENDPOINT:http://127.0.0.1:8848} + metadata: + group: red + + rest: + address: 0.0.0.0:9092 + + publicKey: + accessControl: + enabled: true + excludePathPatterns: '/*' diff --git a/demo/demo-nacos/consumer/src/main/resources/log4j2.xml b/demo/demo-nacos/consumer/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-nacos/consumer/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-nacos/gateway/pom.xml b/demo/demo-nacos/gateway/pom.xml new file mode 100644 index 00000000000..f76d407973a --- /dev/null +++ b/demo/demo-nacos/gateway/pom.xml @@ -0,0 +1,107 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-nacos + 3.4.0-SNAPSHOT + + + nacos-gateway + Java Chassis::Demo::NACOS::GATEWAY + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + edge-core + + + org.apache.servicecomb + registry-nacos + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-nacos/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java b/demo/demo-nacos/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java new file mode 100644 index 00000000000..7d58caafd9d --- /dev/null +++ b/demo/demo-nacos/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class GatewayApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-nacos/gateway/src/main/resources/application.yml b/demo/demo-nacos/gateway/src/main/resources/application.yml new file mode 100644 index 00000000000..3bf90e50b73 --- /dev/null +++ b/demo/demo-nacos/gateway/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-nacos + version: 0.0.1 + name: gateway + registry: + nacos: + enabled: true + serverAddr: ${PAAS_CSE_NACOS_ENDPOINT:http://127.0.0.1:8848} + + rest: + address: 0.0.0.0:9090?sslEnabled=false + + http: + dispatcher: + edge: + default: + enabled: false + url: + enabled: true + pattern: /(.*) + mappings: + consumer: + prefixSegmentCount: 0 + path: "/.*" + microserviceName: consumer + versionRule: 0.0.0+ diff --git a/demo/demo-nacos/gateway/src/main/resources/log4j2.xml b/demo/demo-nacos/gateway/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-nacos/gateway/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-nacos/pom.xml b/demo/demo-nacos/pom.xml new file mode 100644 index 00000000000..a37e292de1d --- /dev/null +++ b/demo/demo-nacos/pom.xml @@ -0,0 +1,61 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-nacos + Java Chassis::Demo::NACOS + pom + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + solution-basic + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-api + + + + + provider + consumer + gateway + test-client + + + diff --git a/demo/demo-nacos/provider/pom.xml b/demo/demo-nacos/provider/pom.xml new file mode 100644 index 00000000000..a23bc1bb06f --- /dev/null +++ b/demo/demo-nacos/provider/pom.xml @@ -0,0 +1,108 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-nacos + 3.4.0-SNAPSHOT + + + nacos-provider + Java Chassis::Demo::NACOS::PROVIDER + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + registry-nacos + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java b/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java new file mode 100644 index 00000000000..a4475d80bda --- /dev/null +++ b/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchema; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchema.class) +public class HeaderParamWithListSchema implements IHeaderParamWithListSchema { + @Override + public String headerListDefault(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListCSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListMULTI(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListSSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListPIPES(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } +} diff --git a/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java b/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java new file mode 100644 index 00000000000..fde6f36b2e3 --- /dev/null +++ b/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ProviderApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ProviderApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java b/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java new file mode 100644 index 00000000000..0076440d232 --- /dev/null +++ b/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.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.servicecomb.samples; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RestSchema(schemaId = "ProviderController") +@RequestMapping(path = "/") +public class ProviderController { + // a very simple service to echo the request parameter + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { + return "Hello " + name; + } + + @GetMapping("/authIncludePath") + public String authIncludePath() { + return "success."; + } +} diff --git a/demo/demo-nacos/provider/src/main/resources/application.yml b/demo/demo-nacos/provider/src/main/resources/application.yml new file mode 100644 index 00000000000..ae6e029b701 --- /dev/null +++ b/demo/demo-nacos/provider/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +# spring boot configurations +servicecomb: + service: + application: demo-nacos + version: 0.0.1 + name: provider + registry: + nacos: + enabled: true + serverAddr: ${PAAS_CSE_NACOS_ENDPOINT:http://127.0.0.1:8848} + metadata: + group: green + + rest: + address: 0.0.0.0:9094 + + publicKey: + accessControl: + enabled: true + includePathPatterns: '/authIncludePath' + black: + authPath: + category: property + propertyName: serviceName + rule: consumer* diff --git a/demo/demo-nacos/provider/src/main/resources/log4j2.xml b/demo/demo-nacos/provider/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-nacos/provider/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-nacos/test-client/pom.xml b/demo/demo-nacos/test-client/pom.xml new file mode 100644 index 00000000000..103aeb24d94 --- /dev/null +++ b/demo/demo-nacos/test-client/pom.xml @@ -0,0 +1,200 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-nacos + 3.4.0-SNAPSHOT + + + nacos-test-client + Java Chassis::Demo::NACOS::TEST-CLIENT + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + registry-local + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + nacos/nacos-server:v2.1.2-slim + nacos-server + + alias + + standalone + hostname + + + Tomcat started on port + + + 8848 + + + + + + nacos-server.port:8848 + + + + + nacos-provider:${project.version} + nacos-provider + + alias + + + -Dservicecomb.registry.nacos.serverAddr=http://nacos-server:8848 + + /maven/maven/nacos-provider-${project.version}.jar + + + nacos-server:nacos-server + + + Nacos is ready + + + 9094 + + + + + + 9094:9094 + + + + + nacos-consumer:${project.version} + nacos-consumer + + alias + + + -Dservicecomb.registry.nacos.serverAddr=http://nacos-server:8848 + + /maven/maven/nacos-consumer-${project.version}.jar + + + nacos-server:nacos-server + + + Nacos is ready + + + 9092 + + + + + + 9092:9092 + + + + + nacos-gateway:${project.version} + nacos-gateway + + alias + + + -Dservicecomb.registry.nacos.serverAddr=http://nacos-server:8848 + + /maven/maven/nacos-gateway-${project.version}.jar + + + nacos-server:nacos-server + + + Nacos is ready + + + 9090 + + + + + + 9090:9090 + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/Config.java b/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/Config.java new file mode 100644 index 00000000000..2e9105cdd4a --- /dev/null +++ b/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/Config.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +public interface Config { + String GATEWAY_URL = "http://localhost:9090"; +} diff --git a/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java b/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java new file mode 100644 index 00000000000..1b129acc2c6 --- /dev/null +++ b/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HeaderParamWithListSchemaIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHeaderListDefault(); + testHeaderListMulti(); + testHeaderListCSV(); + testHeaderListSSV(); + testHeaderListPipes(); + } + + // default to multi + private void testHeaderListDefault() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListDefault", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListPipes() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a|b|c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListPIPES", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListSSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a b c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListSSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListCSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a,b,c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + + headers.add("headerList", "a, b, c"); + entity = new HttpEntity<>(headers); + result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListMulti() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListMULTI", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } +} diff --git a/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java b/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java new file mode 100644 index 00000000000..ad9460753a8 --- /dev/null +++ b/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HelloWorldIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHelloWorld(); + testAuthIncludePath(); + } + + private void testHelloWorld() { + String result = template + .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); + TestMgr.check("Hello World", result); + } + + private void testAuthIncludePath() { + String result = ""; + try { + result = template.getForObject(Config.GATEWAY_URL + "/authIncludePath", String.class); + } catch (Exception e) { + result = "failed"; + } + TestMgr.check("failed", result); + } +} diff --git a/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java b/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java new file mode 100644 index 00000000000..26a2a491bbe --- /dev/null +++ b/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.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.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCaseRunner; +import org.apache.servicecomb.demo.TestMgr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class TestClientApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); + + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args); + + run(); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + } + + public static void run() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); + } +} diff --git a/demo/demo-nacos/test-client/src/main/resources/application.yml b/demo/demo-nacos/test-client/src/main/resources/application.yml new file mode 100644 index 00000000000..2e8b29356b3 --- /dev/null +++ b/demo/demo-nacos/test-client/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-nacos + name: test-client + version: 0.0.1 + + rest: + address: 0.0.0.0:9097 # should be same with server.port to use web container diff --git a/demo/demo-nacos/test-client/src/main/resources/log4j2.xml b/demo/demo-nacos/test-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-nacos/test-client/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-nacos/test-client/src/test/java/org/apache/servicecomb/samples/NocasIT.java b/demo/demo-nacos/test-client/src/test/java/org/apache/servicecomb/samples/NocasIT.java new file mode 100644 index 00000000000..bc7b5a6b9ba --- /dev/null +++ b/demo/demo-nacos/test-client/src/test/java/org/apache/servicecomb/samples/NocasIT.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.servicecomb.samples; + +import org.apache.servicecomb.demo.TestMgr; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = TestClientApplication.class) +public class NocasIT { + + @BeforeEach + public void setUp() { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + TestClientApplication.run(); + + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} diff --git a/demo/demo-pojo/pojo-client/pom.xml b/demo/demo-pojo/pojo-client/pom.xml index 5ff64a3ba4e..582c8c8f511 100644 --- a/demo/demo-pojo/pojo-client/pom.xml +++ b/demo/demo-pojo/pojo-client/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-pojo - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT pojo-client Java Chassis::Demo::POJO::Client @@ -32,10 +32,6 @@ org.apache.servicecomb.demo demo-schema - - org.apache.servicecomb - provider-pojo - org.apache.servicecomb metrics-core @@ -58,24 +54,88 @@ pojo-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/BeanRpcTest.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/BeanRpcTest.java index f5b1368f77c..caadd95efe7 100644 --- a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/BeanRpcTest.java +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/BeanRpcTest.java @@ -20,7 +20,7 @@ import org.apache.servicecomb.provider.pojo.RpcReference; public class BeanRpcTest { - @RpcReference(microserviceName = "pojo") + @RpcReference(microserviceName = "pojo", schemaId = "server") private Test test; public BeanRpcTest() { diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/ClientInterfaceForServerTest.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/ClientInterfaceForServerTest.java new file mode 100644 index 00000000000..34399d081d1 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/ClientInterfaceForServerTest.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.servicecomb.demo.pojo.client; + +import org.apache.servicecomb.demo.server.TestRequest; +import org.apache.servicecomb.demo.server.User; + +public interface ClientInterfaceForServerTest { + User splitParam(int nameNotIndex, User user); + + User wrapParam(TestRequest nameNotRequest); +} diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClient.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClient.java index dbc1bc2eb1e..601901e5884 100644 --- a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClient.java +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClient.java @@ -94,7 +94,7 @@ private void testCodeFirstCompletableFuture(CodeFirstPojoIntf codeFirst) { return; } - Vertx vertx = VertxUtils.getOrCreateVertxByName("transport", null); + Vertx vertx = VertxUtils.getOrCreateVertxByName("transport", null, null); CountDownLatch latch = new CountDownLatch(1); // vertx.runOnContext in normal thread is not a good practice // here just a test, not care for this. @@ -107,7 +107,9 @@ private void testCodeFirstCompletableFuture(CodeFirstPojoIntf codeFirst) { .thenCompose(result -> { TestMgr.check("someone sayhi, context k: v", result); - TestMgr.check(true, context == ContextUtils.getInvocationContext()); + // new call need set invocation context implicitly or will not + // inherit parent context + ContextUtils.setInvocationContext(context); return ((CodeFirstPojoClientIntf) codeFirst).sayHiAsync("someone 1"); }) diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClientIntf.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClientIntf.java index 0d19638f2a1..0f4b6586163 100644 --- a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClientIntf.java +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClientIntf.java @@ -20,10 +20,10 @@ import org.apache.servicecomb.demo.CodeFirstPojoIntf; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; public interface CodeFirstPojoClientIntf extends CodeFirstPojoIntf { - @ApiOperation(nickname = "sayHi", value = "") + @Operation(operationId = "sayHi", summary = "") CompletableFuture sayHiAsync(String name); String sayHi2(String name); diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/PojoClient.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/PojoClient.java index 52ac01f064c..03fafced0ae 100644 --- a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/PojoClient.java +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/PojoClient.java @@ -18,7 +18,6 @@ package org.apache.servicecomb.demo.pojo.client; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,54 +25,46 @@ import java.util.concurrent.ForkJoinPool; import java.util.stream.IntStream; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.DemoConst; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.server.Test; -import org.apache.servicecomb.demo.server.TestRequest; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.demo.smartcare.Application; import org.apache.servicecomb.demo.smartcare.Group; import org.apache.servicecomb.demo.smartcare.SmartCare; import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; import org.apache.servicecomb.provider.pojo.RpcReference; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.annotation.ImportResource; @SpringBootApplication -@EnableServiceComb +@ImportResource(value = "classpath*:META-INF/spring/*.bean.xml") public class PojoClient { private static final Logger LOGGER = LoggerFactory.getLogger(PojoClient.class); - public static final byte[] buffer = new byte[1024]; - // reference a not exist a microservice, and never use it // this should not cause problems @RpcReference(microserviceName = "notExist") public static Test notExist; - @RpcReference(microserviceName = "pojo") + @RpcReference(microserviceName = "pojo", schemaId = "server") public static Test test; public static Test testFromXml; private static SmartCare smartcare; - static { - Arrays.fill(buffer, (byte) 1); - } - public static void setTestFromXml(Test testFromXml) { PojoClient.testFromXml = testFromXml; } @@ -83,12 +74,14 @@ public static void main(String[] args) throws Exception { try { run(); - } catch (Exception e) { + } catch (Throwable e) { TestMgr.check("success", "failed"); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } + TestMgr.summary(); + LOGGER.info("-------------- last time updated checks(maybe more/less): 785 -------------"); } private static void testContextClassLoaderIsNull() throws Exception { @@ -112,20 +105,12 @@ public static void run() throws Exception { String microserviceName = "pojo"; for (String transport : DemoConst.transports) { - ArchaiusUtils.setProperty("servicecomb.references.transport." + microserviceName, transport); + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); LOGGER.info("test {}, transport {}", microserviceName, transport); testContextClassLoaderIsNull(); testNull(testFromXml); - testNull(test); - - // This test case shows destroy of WeightedResponseTimeRule timer task. after test finished will not print: - // "Weight adjusting job started" and thread "NFLoadBalancer-serverWeightTimer-unknown" destroyed. - ArchaiusUtils.setProperty("servicecomb.loadbalance.strategy.name", "WeightedResponse"); - testStringArray(test); - ArchaiusUtils.setProperty("servicecomb.loadbalance.strategy.name", "RoundRobin"); - testStringArray(test); boolean checkerDestroyed = true; // Timer cancel may not destroy thread very fast so check for 3 times. @@ -141,43 +126,22 @@ public static void run() throws Exception { } TestMgr.check(checkerDestroyed, true); - testChinese(test); - testStringHaveSpace(test); - testWrapParam(test); - testSplitParam(test); - testInputArray(test); - - testException(test); - testSmartCare(smartcare); testCommonInvoke(transport); if ("rest".equals(transport)) { testTraceIdOnNotSetBefore(); - testNullRest(test); - testExceptionRest(test); - testEmptyRest(test); - } else if ("highway".equals(transport)) { - testNullHighway(test); - testEmptyHighway(test); } - testTraceIdOnContextContainsTraceId(); } - - TestMgr.summary(); } private static void testHttpClientsIsOk() { - TestMgr.check(HttpClients.getClient("registry") != null, true); - TestMgr.check(HttpClients.getClient("registry-watch") != null, false); TestMgr.check(HttpClients.getClient("config-center") != null, false); TestMgr.check(HttpClients.getClient("http-transport-client") != null, false); TestMgr.check(HttpClients.getClient("http2-transport-client") != null, true); - TestMgr.check(HttpClients.getClient("registry", false) != null, true); - TestMgr.check(HttpClients.getClient("registry-watch", false) != null, false); TestMgr.check(HttpClients.getClient("config-center", false) != null, false); TestMgr.check(HttpClients.getClient("http-transport-client", false) != null, false); TestMgr.check(HttpClients.getClient("http2-transport-client", false) != null, true); @@ -194,7 +158,7 @@ private static void testTraceIdOnNotSetBefore() { private static void testTraceIdOnContextContainsTraceId() { InvocationContext context = new InvocationContext(); - context.addContext(Const.TRACE_ID_NAME, String.valueOf(Long.MIN_VALUE)); + context.addContext(CoreConst.TRACE_ID_NAME, String.valueOf(Long.MIN_VALUE)); ContextUtils.setInvocationContext(context); String traceId = test.testTraceId(); TestMgr.check(String.valueOf(Long.MIN_VALUE), traceId); @@ -220,131 +184,34 @@ private static void testSmartCare(SmartCare smartCare) { smartCare.delApplication("app0")); } - private static void testExceptionRest(Test test) { - try { - test.testException(456); - } catch (InvocationException e) { - TestMgr.check("456 error", e.getErrorData()); - } - - try { - test.testException(556); - } catch (InvocationException e) { - TestMgr.check("[556 error]", e.getErrorData()); - } - - try { - test.testException(557); - } catch (InvocationException e) { - TestMgr.check("[[557 error]]", e.getErrorData()); - } - } - - private static void testException(Test test) { - try { - test.testException(456); - } catch (InvocationException e) { - TestMgr.check("456 error", e.getErrorData()); - } - - try { - test.testException(556); - } catch (InvocationException e) { - TestMgr.check("[556 error]", e.getErrorData()); - } - - try { - test.testException(557); - } catch (InvocationException e) { - TestMgr.check("[[557 error]]", e.getErrorData()); - } - } - - private static void testInputArray(Test test) { - String result = test.addString(new String[] {"a", "b"}); - LOGGER.info("input array result:{}", result); - TestMgr.check("[a, b]", result); - } - - private static void testSplitParam(Test test) { - User result = test.splitParam(1, new User()); - LOGGER.info("split param result:{}", result); - TestMgr.check("User [name=nameA, users count:0, age=100, index=1]", result); - } - - @SuppressWarnings({"deprecation"}) + @SuppressWarnings("rawtypes") private static void testCommonInvoke(String transport) { Map arguments = new HashMap<>(); arguments.put("index", 2); arguments.put("user", new User()); - Object result = InvokerUtils.syncInvoke("pojo", "server", "splitParam", arguments); + Map warpArguments = new HashMap<>(); + warpArguments.put("splitParamBody", arguments); + User result = InvokerUtils.syncInvoke("pojo", "server", + "splitParam", warpArguments, User.class); TestMgr.check("User [name=nameA, users count:0, age=100, index=2]", result); arguments = new HashMap<>(); arguments.put("index", 3); arguments.put("user", new User()); + warpArguments = new HashMap<>(); + warpArguments.put("splitParamBody", arguments); result = InvokerUtils.syncInvoke("pojo", - "0.0.4", transport, "server", "splitParam", - arguments); + warpArguments, User.class); TestMgr.check("User [name=nameA, users count:0, age=100, index=3]", result); } - private static void testEmptyHighway(Test test) { - TestMgr.check("code is ''", test.getTestString("")); - } - - private static void testEmptyRest(Test test) { - TestMgr.check("code is ''", test.getTestString("")); - } - - private static void testNullRest(Test test) { - TestMgr.check(null, test.wrapParam(null)); - } - - private static void testNullHighway(Test test) { - TestMgr.check("nameA", test.wrapParam(null).getName()); - } - private static void testNull(Test test) { TestMgr.check("code is 'null'", test.getTestString(null)); TestMgr.check(null, test.postTestStatic(2)); TestMgr.check(null, test.patchTestStatic(2)); } - - private static void testChinese(Test test) { - TestMgr.check("code is '测试'", test.getTestString("测试")); - - User user = new User(); - user.setName("名字"); - User result = test.splitParam(1, user); - TestMgr.check("名字, users count:0", result.getName()); - } - - private static void testStringHaveSpace(Test test) { - TestMgr.check("code is 'a b'", test.getTestString("a b")); - } - - private static void testStringArray(Test test) { - // TestMgr.check("arr is '[a, , b]'", test.testStringArray(new String[] {"a", null, "b"})); - TestMgr.check("arr is '[a, b]'", test.testStringArray(new String[] {"a", "b"})); - } - - private static void testWrapParam(Test test) { - User user = new User(); - - TestRequest request = new TestRequest(); - request.setUser(user); - request.setIndex(0); - request.setData(buffer); - request.getUsers().add(user); - - User result = test.wrapParam(request); - LOGGER.info("wrap param result:{}", result); - - TestMgr.check("User [name=nameA, users count:1, age=100, index=0]", result); - } } diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSameService.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSameService.java index 1ccb00c75eb..0f735af07c4 100644 --- a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSameService.java +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSameService.java @@ -22,26 +22,27 @@ import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; -import io.swagger.annotations.SwaggerDefinition; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.servers.Server; @Component public class TestSameService implements CategorizedTestCase { - @SwaggerDefinition(basePath = "/SameInterface1") + @OpenAPIDefinition(servers = {@Server(url = "/SameInterface1")}) public interface SameInterface1 { String sayHello(String name); } - @SwaggerDefinition(basePath = "/SameInterface2") + @OpenAPIDefinition(servers = {@Server(url = "/SameInterface2")}) public interface SameInterface2 { String sayHello(String name); } - @SwaggerDefinition(basePath = "/SameService1") + @OpenAPIDefinition(servers = {@Server(url = "/SameService1")}) public interface SameService1 { String sayHello(String name); } - @SwaggerDefinition(basePath = "/SameService2") + @OpenAPIDefinition(servers = {@Server(url = "/SameService2")}) public interface SameService2 { String sayHello(String name); } diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestTestImpl.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestTestImpl.java new file mode 100644 index 00000000000..8b8fabde08e --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestTestImpl.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.pojo.client; + +import java.util.Arrays; + +import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.server.Test; +import org.apache.servicecomb.demo.server.TestRequest; +import org.apache.servicecomb.demo.server.User; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class TestTestImpl implements CategorizedTestCase { + private static final Logger LOGGER = LoggerFactory.getLogger(TestTestImpl.class); + + private static final byte[] buffer = new byte[1024]; + + static { + Arrays.fill(buffer, (byte) 1); + } + + @RpcReference(microserviceName = "pojo", schemaId = "server") + private Test test; + + @Override + public void testAllTransport() throws Exception { + testNull(test); + + // This test case shows destroy of WeightedResponseTimeRule timer task. after test finished will not print: + // "Weight adjusting job started" and thread "NFLoadBalancer-serverWeightTimer-unknown" destroyed. + InMemoryDynamicPropertiesSource.update("servicecomb.loadbalance.strategy.name", "WeightedResponse"); + testStringArray(test); + InMemoryDynamicPropertiesSource.update("servicecomb.loadbalance.strategy.name", "RoundRobin"); + testStringArray(test); + + testChinese(test); + testStringHaveSpace(test); + testWrapParam(test); + + testSplitParam(test); + testInputArray(test); + + testException(test); + + testIntArray(test); + } + + @Override + public void testRestTransport() throws Exception { + testNullRest(test); + testExceptionRest(test); + testEmptyRest(test); + } + + @Override + public void testHighwayTransport() throws Exception { + testNullHighway(test); + testEmptyHighway(test); + } + + private static void testIntArray(Test test) { + int[] request = new int[] {5, 11, 4}; + int[] result = test.testIntArray(request); + TestMgr.check(request.length, result.length); + TestMgr.check(request[1], result[1]); + } + + private static void testEmptyHighway(Test test) { + TestMgr.check("code is ''", test.getTestString("")); + } + + private static void testEmptyRest(Test test) { + TestMgr.check("code is ''", test.getTestString("")); + } + + private static void testNullRest(Test test) { + TestMgr.check(null, test.wrapParam(null)); + } + + private static void testNullHighway(Test test) { + TestMgr.check("nameA", test.wrapParam(null).getName()); + } + + private static void testNull(Test test) { + TestMgr.check("code is 'null'", test.getTestString(null)); + TestMgr.check(null, test.postTestStatic(2)); + TestMgr.check(null, test.patchTestStatic(2)); + } + + private static void testChinese(Test test) { + TestMgr.check("code is '测试'", test.getTestString("测试")); + + User user = new User(); + user.setName("名字"); + User result = test.splitParam(1, user); + TestMgr.check("名字, users count:0", result.getName()); + } + + private static void testStringHaveSpace(Test test) { + TestMgr.check("code is 'a b'", test.getTestString("a b")); + } + + private static void testStringArray(Test test) { + // TestMgr.check("arr is '[a, , b]'", test.testStringArray(new String[] {"a", null, "b"})); + TestMgr.check("arr is '[a, b]'", test.testStringArray(new String[] {"a", "b"})); + } + + private static void testWrapParam(Test test) { + User user = new User(); + + TestRequest request = new TestRequest(); + request.setUser(user); + request.setIndex(0); + request.setData(buffer); + request.getUsers().add(user); + + User result = test.wrapParam(request); + LOGGER.info("wrap param result:{}", result); + + TestMgr.check("User [name=nameA, users count:1, age=100, index=0]", result); + } + + + private static void testExceptionRest(Test test) { + try { + test.testException(456); + } catch (InvocationException e) { + TestMgr.check("456 error", e.getErrorData()); + } + + try { + test.testException(556); + } catch (InvocationException e) { + TestMgr.check("[556 error]", e.getErrorData()); + } + + try { + test.testException(557); + } catch (InvocationException e) { + TestMgr.check("[[557 error]]", e.getErrorData()); + } + } + + private static void testException(Test test) { + try { + test.testException(456); + } catch (InvocationException e) { + TestMgr.check("456 error", e.getErrorData()); + } + + try { + test.testException(556); + } catch (InvocationException e) { + TestMgr.check("[556 error]", e.getErrorData()); + } + + try { + test.testException(557); + } catch (InvocationException e) { + TestMgr.check("[[557 error]]", e.getErrorData()); + } + } + + private static void testInputArray(Test test) { + String result = test.addString(new String[] {"a", "b"}); + LOGGER.info("input array result:{}", result); + TestMgr.check("[a, b]", result); + } + + private static void testSplitParam(Test test) { + User result = test.splitParam(1, new User()); + LOGGER.info("split param result:{}", result); + TestMgr.check("User [name=nameA, users count:0, age=100, index=1]", result); + } +} diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestWeakPojo.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestWeakPojo.java index 8b1e62038cb..eefc0fd1137 100644 --- a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestWeakPojo.java +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestWeakPojo.java @@ -178,7 +178,6 @@ private void testGenerics() { TestMgr.check("hello", nameListResult.get(0).get(0)); } - @SuppressWarnings({"deprecation"}) private void testDiffName() { TestMgr.check(7, diffNames.differentName(2, 3)); TestMgr.check(8, diffNames2.differentName(2, 3)); @@ -187,6 +186,7 @@ private void testDiffName() { args.put("y", 3); Map swaggerArgs = new HashMap<>(); swaggerArgs.put("differentNameBody", args); - TestMgr.check(7, InvokerUtils.syncInvoke("pojo", "WeakPojo", "differentName", swaggerArgs)); + TestMgr.check(7, InvokerUtils.syncInvoke("pojo", "WeakPojo", + "differentName", swaggerArgs, Integer.class)); } } diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/invoker/TestInvokerEndpoint.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/invoker/TestInvokerEndpoint.java index ca74092662b..025e67a0b49 100644 --- a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/invoker/TestInvokerEndpoint.java +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/invoker/TestInvokerEndpoint.java @@ -27,7 +27,7 @@ import org.springframework.stereotype.Component; @Component -@SuppressWarnings({"unchecked", "rawtypes", "deprecation"}) +@SuppressWarnings({"unchecked", "rawtypes"}) public class TestInvokerEndpoint implements CategorizedTestCase { @Override public void testRestTransport() throws Exception { @@ -52,18 +52,19 @@ private void testInvokerUtilsDiffModelHighway() throws Exception { model.setName("hello"); args.put("request", model); - Map result = (Map) InvokerUtils.syncInvoke("pojo", "0+", "highway", "InvokerEndpoint", "model", args); + Map result = InvokerUtils.syncInvoke("pojo", "highway", "InvokerEndpoint", + "model", args, Map.class); TestMgr.check(model.getCode(), result.get("code")); TestMgr.check(model.getName(), result.get("name")); ClientModel modelResult = InvokerUtils - .syncInvoke("pojo", "0+", "highway", "InvokerEndpoint", "model", args, ClientModel.class); + .syncInvoke("pojo", "highway", "InvokerEndpoint", "model", args, ClientModel.class); TestMgr.check(model.getCode(), modelResult.getCode()); TestMgr.check(model.getName(), modelResult.getName()); CountDownLatch countDownLatch = new CountDownLatch(1); InvokerUtils - .reactiveInvoke("pojo", "0+", "highway", "InvokerEndpoint", "model", args, ClientModel.class, response -> { + .reactiveInvoke("pojo", "highway", "InvokerEndpoint", "model", args, ClientModel.class, response -> { ClientModel reactiveResult = response.getResult(); TestMgr.check(model.getCode(), reactiveResult.getCode()); TestMgr.check(model.getName(), reactiveResult.getName()); @@ -80,17 +81,18 @@ private void testInvokerUtilsDiffModelRest() throws Exception { model.setName("hello"); args.put("request", model); - Map result = (Map) InvokerUtils.syncInvoke("pojo", "0+", "rest", "InvokerEndpoint", "model", args); + Map result = InvokerUtils.syncInvoke("pojo", "rest", "InvokerEndpoint", + "model", args, Map.class); TestMgr.check(model.getCode(), result.get("code")); TestMgr.check(model.getName(), result.get("name")); ClientModel modelResult = InvokerUtils - .syncInvoke("pojo", "0+", "rest", "InvokerEndpoint", "model", args, ClientModel.class); + .syncInvoke("pojo", "rest", "InvokerEndpoint", "model", args, ClientModel.class); TestMgr.check(model.getCode(), modelResult.getCode()); TestMgr.check(model.getName(), modelResult.getName()); CountDownLatch countDownLatch = new CountDownLatch(1); - InvokerUtils.reactiveInvoke("pojo", "0+", "rest", "InvokerEndpoint", "model", args, ClientModel.class, response -> { + InvokerUtils.reactiveInvoke("pojo", "rest", "InvokerEndpoint", "model", args, ClientModel.class, response -> { ClientModel reactiveResult = response.getResult(); TestMgr.check(model.getCode(), reactiveResult.getCode()); TestMgr.check(model.getName(), reactiveResult.getName()); @@ -107,7 +109,8 @@ private void testInvokerUtilsDiffModel() throws Exception { model.setName("hello"); args.put("request", model); - Map result = (Map) InvokerUtils.syncInvoke("pojo", "InvokerEndpoint", "model", args); + Map result = InvokerUtils.syncInvoke("pojo", "InvokerEndpoint", + "model", args, Map.class); TestMgr.check(model.getCode(), result.get("code")); TestMgr.check(model.getName(), result.get("name")); @@ -116,7 +119,8 @@ private void testInvokerUtilsDiffModel() throws Exception { TestMgr.check(model.getName(), modelResult.getName()); CountDownLatch countDownLatch = new CountDownLatch(1); - InvokerUtils.reactiveInvoke("pojo", "InvokerEndpoint", "model", args, ClientModel.class, response -> { + InvokerUtils.reactiveInvoke("pojo", "InvokerEndpoint", "model", args, + ClientModel.class, response -> { ClientModel reactiveResult = response.getResult(); TestMgr.check(model.getCode(), reactiveResult.getCode()); TestMgr.check(model.getName(), reactiveResult.getName()); @@ -133,7 +137,7 @@ private void testInvokerUtilsDiffModelMapArgs() throws Exception { model.put("name", "hello"); args.put("request", model); - Map result = (Map) InvokerUtils.syncInvoke("pojo", "InvokerEndpoint", "model", args); + Map result = InvokerUtils.syncInvoke("pojo", "InvokerEndpoint", "model", args, Map.class); TestMgr.check(model.get("code"), result.get("code")); TestMgr.check(model.get("name"), result.get("name")); diff --git a/demo/demo-pojo/pojo-client/src/main/resources/microservice.yaml b/demo/demo-pojo/pojo-client/src/main/resources/microservice.yaml index 5615e80523d..45c62dffcf1 100644 --- a/demo/demo-pojo/pojo-client/src/main/resources/microservice.yaml +++ b/demo/demo-pojo/pojo-client/src/main/resources/microservice.yaml @@ -15,20 +15,14 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: pojotest -service_description: - name: pojoClient - version: 0.0.1 servicecomb: service: - registry: + application: pojotest + name: pojoClient + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 - instance: - healthCheck: - interval: 2 - watch: false - empty: - protection: true rest.client.enabled: false # using only http2 swagger: disableDataTypeCheck: true @@ -43,6 +37,7 @@ servicecomb: window_time: 12000 publisher.defaultLog.enabled: false # when in testing , can turn on flowcontrol: + enabled: true Consumer: qps: limit: diff --git a/demo/demo-pojo/pojo-server/pom.xml b/demo/demo-pojo/pojo-server/pom.xml index 08d77fe54c1..b0b673ef47e 100644 --- a/demo/demo-pojo/pojo-server/pom.xml +++ b/demo/demo-pojo/pojo-server/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-pojo - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT pojo-server Java Chassis::Demo::POJO::Server @@ -32,15 +32,13 @@ org.apache.servicecomb.demo demo-schema - - org.apache.servicecomb - provider-pojo - org.apache.servicecomb.demo.pojo.server.PojoServer + true + @@ -53,28 +51,51 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/CodeFirstPojo.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/CodeFirstPojo.java index df1111f3ca6..680d7981d05 100644 --- a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/CodeFirstPojo.java +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/CodeFirstPojo.java @@ -32,11 +32,12 @@ import org.apache.servicecomb.provider.pojo.RpcSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.SwaggerDefinition; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.servers.Server; @RpcSchema() -@SwaggerDefinition(basePath = "/pojo/rest") +@OpenAPIDefinition(servers = {@Server(url = "/pojo/rest")}) public class CodeFirstPojo implements CodeFirstPojoIntf { @Override public ParseResponse parse(ParseRequest request) { @@ -128,7 +129,7 @@ public String sayHi(String name) { : ContextUtils.getInvocationContext().getContext("k")); } - @ApiOperation(nickname = "sayHi2", value = "") + @Operation(operationId = "sayHi2", summary = "") public CompletableFuture sayHi2Async(String name) { CompletableFuture future = new CompletableFuture<>(); future.complete(name + " sayhi 2"); diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/PojoServer.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/PojoServer.java index 7d4feb1622e..1ae04e578ee 100644 --- a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/PojoServer.java +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/PojoServer.java @@ -17,15 +17,19 @@ package org.apache.servicecomb.demo.pojo.server; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.annotation.ImportResource; @SpringBootApplication -@EnableServiceComb +@ImportResource(value = "classpath*:META-INF/spring/*.bean.xml") public class PojoServer { public static void main(String[] args) throws Exception { - new SpringApplicationBuilder(PojoServer.class).web(WebApplicationType.NONE).run(args); + try { + new SpringApplicationBuilder(PojoServer.class).web(WebApplicationType.NONE).run(args); + } catch (Throwable e) { + e.printStackTrace(); + } } } diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/TestImpl.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/TestImpl.java index 711f59007e3..230dfd804fe 100644 --- a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/TestImpl.java +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/TestImpl.java @@ -20,7 +20,7 @@ import java.util.Arrays; import java.util.List; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.demo.server.Test; import org.apache.servicecomb.demo.server.TestRequest; import org.apache.servicecomb.demo.server.User; @@ -103,6 +103,11 @@ public String addString(String[] strArr) { @Override public String testTraceId() { - return ContextUtils.getInvocationContext().getContext(Const.TRACE_ID_NAME); + return ContextUtils.getInvocationContext().getContext(CoreConst.TRACE_ID_NAME); + } + + @Override + public int[] testIntArray(int[] request) { + return request; } } diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/WeakPojo.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/WeakPojo.java index fc82bf108ae..962952531d0 100644 --- a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/WeakPojo.java +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/WeakPojo.java @@ -22,13 +22,13 @@ import org.apache.servicecomb.demo.server.GenericsModel; import org.apache.servicecomb.provider.pojo.RpcSchema; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; @RpcSchema(schemaId = "WeakPojo") public class WeakPojo { - @ApiOperation(value = "differentName", nickname = "differentName") - public int diffNames(@ApiParam(name = "x") int a, @ApiParam(name = "y") int b) { + @Operation(summary = "differentName", operationId = "differentName") + public int diffNames(@Parameter(name = "x") int a, @Parameter(name = "y") int b) { return a * 2 + b; } diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameInterface.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameInterface.java index 1ab38bb7cda..340ddf355eb 100644 --- a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameInterface.java +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameInterface.java @@ -17,9 +17,10 @@ package org.apache.servicecomb.demo.pojo.server.same.pk1; -import io.swagger.annotations.SwaggerDefinition; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.servers.Server; -@SwaggerDefinition(basePath = "/SameInterface1") +@OpenAPIDefinition(servers = {@Server(url = "/SameInterface1")}) public interface SameInterface { String sayHello(String name); } diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameService.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameService.java index 08540e1b7a7..9a59104e99c 100644 --- a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameService.java +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameService.java @@ -20,11 +20,12 @@ import org.apache.servicecomb.provider.pojo.RpcSchema; import org.springframework.stereotype.Component; -import io.swagger.annotations.SwaggerDefinition; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.servers.Server; @RpcSchema(schemaId = "SameService1") @Component("SameService1") -@SwaggerDefinition(basePath = "/SameService1") +@OpenAPIDefinition(servers = {@Server(url = "/SameService1")}) public class SameService { public String sayHello(String name) { return "pk1-svc-" + name; diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameInterface.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameInterface.java index ce901b4abf8..7410776ef65 100644 --- a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameInterface.java +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameInterface.java @@ -17,9 +17,10 @@ package org.apache.servicecomb.demo.pojo.server.same.pk2; -import io.swagger.annotations.SwaggerDefinition; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.servers.Server; -@SwaggerDefinition(basePath = "/SameInterface2") +@OpenAPIDefinition(servers = {@Server(url = "/SameInterface2")}) public interface SameInterface { String sayHello(String name); } diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameService.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameService.java index 6ebade7c012..38fc544681c 100644 --- a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameService.java +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameService.java @@ -20,11 +20,12 @@ import org.apache.servicecomb.provider.pojo.RpcSchema; import org.springframework.stereotype.Component; -import io.swagger.annotations.SwaggerDefinition; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.servers.Server; @RpcSchema(schemaId = "SameService2") @Component("SameService2") -@SwaggerDefinition(basePath = "/SameService2") +@OpenAPIDefinition(servers = {@Server(url = "/SameService2")}) public class SameService { public String sayHello(String name) { return "pk2-svc-" + name; diff --git a/demo/demo-pojo/pojo-server/src/main/resources/microservice.yaml b/demo/demo-pojo/pojo-server/src/main/resources/microservice.yaml index c6807772d77..0fe723a06ff 100644 --- a/demo/demo-pojo/pojo-server/src/main/resources/microservice.yaml +++ b/demo/demo-pojo/pojo-server/src/main/resources/microservice.yaml @@ -15,13 +15,13 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: pojotest -service_description: - name: pojo - version: 0.0.4 servicecomb: service: - registry: + application: pojotest + name: pojo + version: 0.0.4 + registry: + sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080?protocol=http2 @@ -34,6 +34,7 @@ servicecomb: disableDataTypeCheck: true flowcontrol: + enabled: true Provider: qps: limit: @@ -41,4 +42,3 @@ servicecomb: FlowControlSchema: foo: 3 bar: 3000 - diff --git a/demo/demo-pojo/pom.xml b/demo/demo-pojo/pom.xml index e9e1b38b7c6..96217173a03 100644 --- a/demo/demo-pojo/pom.xml +++ b/demo/demo-pojo/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-pojo Java Chassis::Demo::POJO @@ -32,6 +32,10 @@ pojo-client + + org.apache.servicecomb + solution-basic + org.apache.servicecomb registry-service-center diff --git a/demo/demo-register-url-prefix/README.md b/demo/demo-register-url-prefix/README.md index 416a098a41b..e0b2df4db0b 100644 --- a/demo/demo-register-url-prefix/README.md +++ b/demo/demo-register-url-prefix/README.md @@ -1,4 +1,4 @@ -This demo is an integration test for testing +This demo is an integration test for testing ```yaml servicecomb: @@ -7,7 +7,7 @@ servicecomb: registerUrlPrefix: true ``` -When this configuration is enabled, web context path is added to swagger, and consumer can +When this configuration is enabled, web context path is added to swagger, and consumer can invoke with context path ```yaml diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-client/pom.xml b/demo/demo-register-url-prefix/demo-register-url-prefix-client/pom.xml index af2d62dc762..1b7fc782cd1 100644 --- a/demo/demo-register-url-prefix/demo-register-url-prefix-client/pom.xml +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-client/pom.xml @@ -25,17 +25,13 @@ org.apache.servicecomb.demo demo-register-url-prefix - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT org.apache.servicecomb.demo.prefix.Application - - org.apache.servicecomb - java-chassis-spring-boot-starter-servlet - org.apache.servicecomb.demo demo-schema @@ -49,24 +45,88 @@ demo-register-url-prefix-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/java/org/apache/servicecomb/demo/prefix/Application.java b/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/java/org/apache/servicecomb/demo/prefix/Application.java index feb7b3deb16..8b53c6c6bd4 100644 --- a/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/java/org/apache/servicecomb/demo/prefix/Application.java +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/java/org/apache/servicecomb/demo/prefix/Application.java @@ -19,14 +19,12 @@ import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @SpringBootApplication -@EnableServiceComb public class Application { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(Application.class).web(WebApplicationType.SERVLET).build().run(args); @@ -35,7 +33,7 @@ public static void main(final String[] args) throws Exception { } public static void runTest() { - RestTemplate template = RestTemplateBuilder.create(); + RestOperations template = RestTemplateBuilder.create(); TestMgr.check("2", template .getForObject("cse://demo-register-url-prefix-server/hellodemo/register/url/prefix/getName?name=2", String.class)); diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/application.yml b/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/application.yml index 4516d6cd260..65b87064740 100644 --- a/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/application.yml +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/application.yml @@ -18,17 +18,15 @@ server: port: 8082 -# java-chassis configurations - -APPLICATION_ID: demo-register-url-prefix -service_description: - name: demo-register-url-prefix-client - version: 0.0.1 servicecomb: service: - registry: + application: demo-register-url-prefix + name: demo-register-url-prefix-client + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 handler: chain: Consumer: - default: loadbalance \ No newline at end of file + default: loadbalance diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-server/pom.xml b/demo/demo-register-url-prefix/demo-register-url-prefix-server/pom.xml index 83401874901..2d34ae62051 100644 --- a/demo/demo-register-url-prefix/demo-register-url-prefix-server/pom.xml +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-server/pom.xml @@ -25,30 +25,17 @@ org.apache.servicecomb.demo demo-register-url-prefix - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT org.apache.servicecomb.demo.prefix.PrefixApplication - - org.apache.servicecomb - registry-service-center - jakarta.ws.rs jakarta.ws.rs-api - - org.apache.servicecomb - java-chassis-spring-boot-starter-servlet - - - org.apache.servicecomb - inspector - - @@ -63,28 +50,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/PrefixApplication.java b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/PrefixApplication.java index f883f05caa0..2fc02f7adbd 100644 --- a/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/PrefixApplication.java +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/PrefixApplication.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.demo.prefix; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class PrefixApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(PrefixApplication.class).web(WebApplicationType.SERVLET).build().run(args); diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixEndpoint.java b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixEndpoint.java index e383b0b8620..dae91a50f49 100644 --- a/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixEndpoint.java +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixEndpoint.java @@ -17,26 +17,21 @@ package org.apache.servicecomb.demo.prefix; -import javax.ws.rs.core.MediaType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "RegisterUrlPrefixEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class RegisterUrlPrefixEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(RegisterUrlPrefixEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/application.yml b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/application.yml index c951b22c174..8a4140eeced 100644 --- a/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/application.yml +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/application.yml @@ -20,16 +20,14 @@ server: context-path: /hellodemo port: 8080 -# java-chassis configurations - -APPLICATION_ID: demo-register-url-prefix -service_description: - name: demo-register-url-prefix-server - version: 0.0.1 servicecomb: service: - registry: + application: demo-register-url-prefix + name: demo-register-url-prefix-server + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 - registerUrlPrefix: true + registerUrlPrefix: true rest: address: 0.0.0.0:8080 diff --git a/demo/demo-register-url-prefix/pom.xml b/demo/demo-register-url-prefix/pom.xml index fff7bf325b4..e51630b5397 100644 --- a/demo/demo-register-url-prefix/pom.xml +++ b/demo/demo-register-url-prefix/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-register-url-prefix Java Chassis::Demo::Register URL Prefix @@ -33,6 +33,10 @@ + + org.apache.servicecomb + solution-basic + org.apache.servicecomb registry-service-center diff --git a/demo/demo-schema/pom.xml b/demo/demo-schema/pom.xml index 3f96549473f..b17f345111b 100644 --- a/demo/demo-schema/pom.xml +++ b/demo/demo-schema/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-schema Java Chassis::Demo::Schema @@ -30,46 +30,13 @@ org.apache.servicecomb - handler-loadbalance - - - org.apache.servicecomb - transport-highway - - - org.apache.servicecomb - transport-rest-vertx - - - org.apache.servicecomb - transport-rest-servlet - - - jakarta.servlet - jakarta.servlet-api - compile - - - org.apache.servicecomb - handler-flowcontrol-qps - - - org.apache.servicecomb - swagger-generator-springmvc + solution-basic org.apache.servicecomb foundation-test-scaffolding compile - - org.apache.servicecomb - inspector - - - javax.xml.bind - jaxb-api - diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CategorizedTestCaseRunner.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CategorizedTestCaseRunner.java index 47dd8b973bf..acb82190374 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CategorizedTestCaseRunner.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CategorizedTestCaseRunner.java @@ -19,8 +19,8 @@ import java.util.Map; +import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,17 +49,14 @@ public static void runCategorizedTestCase(String microserviceName) throws Except LOGGER.error("run categorized test case " + testCase.getClass().getName() + " failed.", e); - TestMgr.failed("run categorized test case " + - testCase.getClass().getName() + - " failed, reason " + e.getMessage(), - e); + throw e; } } } } public static void changeTransport(String microserviceName, String transport) { - ArchaiusUtils.setProperty("servicecomb.references.transport." + microserviceName, transport); + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); } } diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CodeFirstRestTemplate.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CodeFirstRestTemplate.java index dc0061b8622..ee2bf395847 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CodeFirstRestTemplate.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CodeFirstRestTemplate.java @@ -21,36 +21,46 @@ import java.util.HashMap; import java.util.Map; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.demo.ignore.InputModelForTestIgnore; import org.apache.servicecomb.demo.ignore.OutputModelForTestIgnore; import org.apache.servicecomb.demo.server.User; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.apache.servicecomb.registry.RegistrationManager; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.env.Environment; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; import io.vertx.core.json.JsonObject; public class CodeFirstRestTemplate { + protected Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + protected void changeTransport(String microserviceName, String transport) { - ArchaiusUtils.setProperty("servicecomb.references.transport." + microserviceName, transport); + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); } - public void testCodeFirst(RestTemplate template, String microserviceName, String basePath) { + public void testCodeFirst(RestOperations template, String microserviceName, String basePath) { String cseUrlPrefix = "cse://" + microserviceName + basePath; changeTransport(microserviceName, "highway"); testOnlyHighway(template, cseUrlPrefix); - changeTransport(microserviceName, Const.RESTFUL); + changeTransport(microserviceName, CoreConst.RESTFUL); testOnlyRest(microserviceName, template, cseUrlPrefix); for (String transport : DemoConst.transports) { @@ -59,7 +69,7 @@ public void testCodeFirst(RestTemplate template, String microserviceName, String } } - protected void testAllTransport(String microserviceName, RestTemplate template, String cseUrlPrefix) { + protected void testAllTransport(String microserviceName, RestOperations template, String cseUrlPrefix) { testCodeFirstUserMap(template, cseUrlPrefix); testCodeFirstTextPlain(template, cseUrlPrefix); testCodeFirstBytes(template, cseUrlPrefix); @@ -80,11 +90,11 @@ protected void testAllTransport(String microserviceName, RestTemplate template, testRawJson(template, cseUrlPrefix); } - protected void testOnlyHighway(RestTemplate template, String cseUrlPrefix) { + protected void testOnlyHighway(RestOperations template, String cseUrlPrefix) { } - protected void testOnlyRest(String microserviceName, RestTemplate template, String cseUrlPrefix) { + protected void testOnlyRest(String microserviceName, RestOperations template, String cseUrlPrefix) { testCodeFirstUserMap(template, cseUrlPrefix); testCodeFirstTextPlain(template, cseUrlPrefix); testCodeFirstBytes(template, cseUrlPrefix); @@ -99,7 +109,7 @@ protected void testOnlyRest(String microserviceName, RestTemplate template, Stri testTraceIdOnNotSetBefore(template, cseUrlPrefix); } - private void testCodeFirstUserMap(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstUserMap(RestOperations template, String cseUrlPrefix) { User user1 = new User(); user1.setNames(new String[] {"u1", "u2"}); @@ -110,10 +120,16 @@ private void testCodeFirstUserMap(RestTemplate template, String cseUrlPrefix) { userMap.put("u1", user1); userMap.put("u2", user2); - @SuppressWarnings("unchecked") - Map result = template.postForObject(cseUrlPrefix + "testUserMap", - userMap, - Map.class); + // TODO: shall we support this usage? Seams not valid, cause result should be Map and type not defined. +// @SuppressWarnings("unchecked") +// Map result = template.postForObject(cseUrlPrefix + "testUserMap", +// userMap, +// Map.class); + + Map result = template.exchange(cseUrlPrefix + "testUserMap", HttpMethod.POST, + new HttpEntity<>(userMap), + new ParameterizedTypeReference>() { + }).getBody(); TestMgr.check("u1", result.get("u1").getNames()[0]); TestMgr.check("u2", result.get("u1").getNames()[1]); @@ -121,7 +137,7 @@ private void testCodeFirstUserMap(RestTemplate template, String cseUrlPrefix) { TestMgr.check("u4", result.get("u2").getNames()[1]); } - private void testCodeFirstTextPlain(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstTextPlain(RestOperations template, String cseUrlPrefix) { String body = "a=1"; String result = template.postForObject(cseUrlPrefix + "textPlain", body, @@ -129,7 +145,7 @@ private void testCodeFirstTextPlain(RestTemplate template, String cseUrlPrefix) TestMgr.check(body, result); } - private void testCodeFirstBytes(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstBytes(RestOperations template, String cseUrlPrefix) { byte[] body = new byte[] {0, 1, 2}; byte[] result = template.postForObject(cseUrlPrefix + "bytes", body, @@ -140,13 +156,13 @@ private void testCodeFirstBytes(RestTemplate template, String cseUrlPrefix) { TestMgr.check(2, result[2]); } - protected void checkStatusCode(String microserviceName, int expectStatusCode, HttpStatus httpStatus) { + protected void checkStatusCode(String microserviceName, int expectStatusCode, HttpStatusCode httpStatus) { TestMgr.check(expectStatusCode, httpStatus.value()); } - private void testCseResponse(String targetMicroserviceName, RestTemplate template, + private void testCseResponse(String targetMicroserviceName, RestOperations template, String cseUrlPrefix) { - String srcMicroserviceName = RegistrationManager.INSTANCE.getMicroservice().getServiceName(); + String srcMicroserviceName = BootStrapProperties.readServiceName(environment); ResponseEntity responseEntity = template.exchange(cseUrlPrefix + "cseResponse", HttpMethod.GET, null, User.class); @@ -156,7 +172,7 @@ private void testCseResponse(String targetMicroserviceName, RestTemplate templat checkStatusCode(targetMicroserviceName, 202, responseEntity.getStatusCode()); } - private void testCodeFirstAddDate(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstAddDate(RestOperations template, String cseUrlPrefix) { Map body = new HashMap<>(); Date date = new Date(); body.put("date", date); @@ -169,7 +185,7 @@ private void testCodeFirstAddDate(RestTemplate template, String cseUrlPrefix) { TestMgr.check(new Date(date.getTime() + seconds * 1000), result); } - private void testCodeFirstAddString(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstAddString(RestOperations template, String cseUrlPrefix) { ResponseEntity responseEntity = template.exchange(cseUrlPrefix + "addstring?s=a&s=b", HttpMethod.DELETE, @@ -178,25 +194,25 @@ private void testCodeFirstAddString(RestTemplate template, String cseUrlPrefix) TestMgr.check("ab", responseEntity.getBody()); } - private void testCodeFirstIsTrue(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstIsTrue(RestOperations template, String cseUrlPrefix) { boolean result = template.getForObject(cseUrlPrefix + "istrue", boolean.class); TestMgr.check(true, result); } - private void testCodeFirstSayHi2(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstSayHi2(RestOperations template, String cseUrlPrefix) { ResponseEntity responseEntity = template.exchange(cseUrlPrefix + "sayhi/{name}/v2", HttpMethod.PUT, null, String.class, "world"); TestMgr.check("world sayhi 2", responseEntity.getBody()); } - private void testCodeFirstSayHi(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstSayHi(RestOperations template, String cseUrlPrefix) { ResponseEntity responseEntity = template.exchange(cseUrlPrefix + "sayhi/{name}", HttpMethod.PUT, null, String.class, "world"); - TestMgr.check(202, responseEntity.getStatusCodeValue()); + TestMgr.check(202, responseEntity.getStatusCode().value()); TestMgr.check("world sayhi", responseEntity.getBody()); } - private void testCodeFirstSaySomething(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstSaySomething(RestOperations template, String cseUrlPrefix) { Person person = new Person(); person.setName("person name"); @@ -209,7 +225,7 @@ private void testCodeFirstSaySomething(RestTemplate template, String cseUrlPrefi TestMgr.check("prefix prefix person name", result); } - private void testCodeFirstSayHello(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstSayHello(RestOperations template, String cseUrlPrefix) { Map persionFieldMap = new HashMap<>(); persionFieldMap.put("name", "person name from map"); Person result = template.postForObject(cseUrlPrefix + "sayhello", persionFieldMap, Person.class); @@ -221,7 +237,7 @@ private void testCodeFirstSayHello(RestTemplate template, String cseUrlPrefix) { TestMgr.check("hello person name from Object", result); } - private void testCodeFirstAdd(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstAdd(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "3"); @@ -230,7 +246,7 @@ private void testCodeFirstAdd(RestTemplate template, String cseUrlPrefix) { TestMgr.check(8, result); } - private void testCodeFirstReduce(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstReduce(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); @@ -243,7 +259,7 @@ private void testCodeFirstReduce(RestTemplate template, String cseUrlPrefix) { TestMgr.check(2, result.getBody()); } - private void testModelFieldIgnore(RestTemplate template, String cseUrlPrefix) { + private void testModelFieldIgnore(RestOperations template, String cseUrlPrefix) { InputModelForTestIgnore input = new InputModelForTestIgnore("input_id_rest", "input_id_content", new Person("inputSomeone"), new JsonObject("{\"InputJsonKey\" : \"InputJsonValue\"}"), () -> { }); @@ -263,22 +279,22 @@ private void testModelFieldIgnore(RestTemplate template, String cseUrlPrefix) { TestMgr.check(null, output.getOutputObject()); } - private void testRawJson(RestTemplate template, String cseUrlPrefix) { + private void testRawJson(RestOperations template, String cseUrlPrefix) { String input = "{\"name\" : \"zyy\"}"; String output = template.postForObject(cseUrlPrefix + "rawJsonAnnotation", input, String.class); TestMgr.check("hello zyy", output); } - private void testTraceIdOnNotSetBefore(RestTemplate template, String cseUrlPrefix) { + private void testTraceIdOnNotSetBefore(RestOperations template, String cseUrlPrefix) { String traceIdUrl = cseUrlPrefix + "traceId"; String result = template.getForObject(traceIdUrl, String.class); TestMgr.checkNotEmpty(result); } - private void testTraceIdOnContextContainsTraceId(RestTemplate template, String cseUrlPrefix) { + private void testTraceIdOnContextContainsTraceId(RestOperations template, String cseUrlPrefix) { String traceIdUrl = cseUrlPrefix + "traceId"; InvocationContext invocationContext = new InvocationContext(); - invocationContext.addContext(Const.TRACE_ID_NAME, String.valueOf(Long.MIN_VALUE)); + invocationContext.addContext(CoreConst.TRACE_ID_NAME, String.valueOf(Long.MIN_VALUE)); ContextUtils.setInvocationContext(invocationContext); String result = template.getForObject(traceIdUrl, String.class); TestMgr.check(String.valueOf(Long.MIN_VALUE), result); diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CommonSchemaInterface.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CommonSchemaInterface.java index eef63ec006b..550f9e5adaf 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CommonSchemaInterface.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CommonSchemaInterface.java @@ -22,7 +22,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; @RequestMapping("/CommonSchemaInterface") public interface CommonSchemaInterface { @@ -30,7 +30,7 @@ public interface CommonSchemaInterface { String testInvocationTimeout(@RequestParam("timeout") long timeout, @RequestParam("name") String name); @GetMapping(path = "testInvocationTimeoutWithInvocation") - @ApiOperation(value = "testInvocationTimeoutWithInvocation", nickname = "testInvocationTimeoutWithInvocation") + @Operation(summary = "testInvocationTimeoutWithInvocation", operationId = "testInvocationTimeoutWithInvocation") String testInvocationTimeout(InvocationContext context, @RequestParam("timeout") long timeout, @RequestParam("name") String name); diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/DemoConst.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/DemoConst.java index c76d4c81a33..84018a5254b 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/DemoConst.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/DemoConst.java @@ -17,8 +17,8 @@ package org.apache.servicecomb.demo; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; public interface DemoConst { - String[] transports = new String[] {"highway", "rest", Const.ANY_TRANSPORT}; + String[] transports = new String[] {"rest", "highway", CoreConst.ANY_TRANSPORT}; } diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/TestMgr.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/TestMgr.java index 9cfffaa5c2f..c90fe8bc1a5 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/TestMgr.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/TestMgr.java @@ -32,7 +32,7 @@ public class TestMgr { private static String msg = ""; - private static final AtomicLong checkes = new AtomicLong(0); + private static final AtomicLong checks = new AtomicLong(0); public static void setMsg(String msg) { TestMgr.msg = msg; @@ -47,7 +47,7 @@ public static void check(Object expect, Object real) { } public static void check(Object expect, Object real, Throwable error) { - checkes.incrementAndGet(); + checks.incrementAndGet(); if (expect == real) { return; @@ -66,7 +66,7 @@ public static void check(Object expect, Object real, Throwable error) { } public static void checkNotEmpty(String real) { - checkes.incrementAndGet(); + checks.incrementAndGet(); if (StringUtils.isEmpty(real)) { errorList.add(new Error(msg + " | unexpected null result, method is " + getCaller())); @@ -78,7 +78,7 @@ public static void fail(String desc) { } public static void failed(String desc, Throwable e) { - checkes.incrementAndGet(); + checks.incrementAndGet(); Error error = new Error(msg + " | " + desc + ", method is " + getCaller()); if (e != null) { @@ -94,12 +94,12 @@ public static boolean isSuccess() { public static void summary() { if (errorList.isEmpty()) { LOGGER.info("............. test finished ............"); - LOGGER.info("............. total checks : " + checkes.get()); + LOGGER.info("............. total checks : " + checks.get()); return; } LOGGER.info("............. test not finished ............"); - LOGGER.info("............. total checks : " + checkes.get()); + LOGGER.info("............. total checks : " + checks.get()); LOGGER.info("............. total errors : " + errorList.size()); LOGGER.info("............. error details: "); for (Throwable e : errorList) { diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchema.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchema.java new file mode 100644 index 00000000000..8ee60ff27dd --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchema.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.api; + +import java.util.List; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.Explode; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.enums.ParameterStyle; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.Path; + +@Path("/headerList") +public interface IHeaderParamWithListSchema { + @Path("headerListDefault") + @GET + String headerListDefault( + @HeaderParam("headerList") List headerList); + + @Path("headerListCSV") + @GET + String headerListCSV( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.FORM, explode = Explode.FALSE) + List headerList); + + @Path("headerListMULTI") + @GET + String headerListMULTI( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.FORM, explode = Explode.TRUE) + List headerList); + + @Path("headerListSSV") + @GET + String headerListSSV( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.SPACEDELIMITED, explode = Explode.FALSE) + List headerList); + + @Path("headerListPIPES") + @GET + String headerListPIPES( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.PIPEDELIMITED, explode = Explode.FALSE) + List headerList); +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchemaSpringMvc.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchemaSpringMvc.java new file mode 100644 index 00000000000..75875f8790c --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchemaSpringMvc.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.servicecomb.demo.api; + + +import java.util.List; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.Explode; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.enums.ParameterStyle; + +@RequestMapping("/headerList") +public interface IHeaderParamWithListSchemaSpringMvc { + @GetMapping("headerListDefault") + String headerListDefault( + @RequestHeader("headerList") List headerList); + + @GetMapping("headerListCSV") + String headerListCSV( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.FORM, explode = Explode.FALSE) + List headerList); + + @GetMapping("headerListMULTI") + String headerListMULTI( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.FORM, explode = Explode.TRUE) + List headerList); + + @GetMapping("headerListSSV") + String headerListSSV( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.SPACEDELIMITED, explode = Explode.FALSE) + List headerList); + + @GetMapping("headerListPIPES") + String headerListPIPES( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.PIPEDELIMITED, explode = Explode.FALSE) + List headerList); +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/PersonAlias.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/PersonAlias.java new file mode 100644 index 00000000000..a1766eb29c2 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/PersonAlias.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.servicecomb.demo.controller; + +public class PersonAlias { + private String name; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBJob.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBJob.java index 620879803ab..797833437a7 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBJob.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBJob.java @@ -16,11 +16,12 @@ */ package org.apache.servicecomb.demo.jaxbbean; -import javax.xml.bind.annotation.XmlAccessOrder; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorOrder; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; + +import jakarta.xml.bind.annotation.XmlAccessOrder; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorOrder; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "job") @XmlAccessorType(XmlAccessType.FIELD) diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBPerson.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBPerson.java index bd66e9dbd9f..450ebacabe8 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBPerson.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBPerson.java @@ -18,13 +18,13 @@ import java.io.Serializable; -import javax.xml.bind.annotation.XmlAccessOrder; -import javax.xml.bind.annotation.XmlAccessorOrder; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; -import javax.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.XmlAccessOrder; +import jakarta.xml.bind.annotation.XmlAccessorOrder; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; +import jakarta.xml.bind.annotation.XmlType; @XmlType(propOrder = {"name", "role", "job"}) @XmlRootElement(name = "person") diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxrs/server/validation/ValidationModel.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxrs/server/validation/ValidationModel.java index 09e26129b7e..7a99260c393 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxrs/server/validation/ValidationModel.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxrs/server/validation/ValidationModel.java @@ -19,7 +19,7 @@ import java.util.List; -import javax.validation.constraints.NotNull; +import jakarta.validation.constraints.NotNull; public class ValidationModel { @NotNull diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/ProduceAppXmlProcessor.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/ProduceAppXmlProcessor.java index 2303e5d7bdb..17659cfed21 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/ProduceAppXmlProcessor.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/ProduceAppXmlProcessor.java @@ -20,7 +20,7 @@ import java.io.OutputStream; import java.nio.charset.StandardCharsets; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.demo.utils.JAXBUtils; diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/override/ProduceAppXmlProcessor.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/override/ProduceAppXmlProcessor.java index 116b15fa65c..aebe13eb116 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/override/ProduceAppXmlProcessor.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/override/ProduceAppXmlProcessor.java @@ -20,7 +20,7 @@ import java.io.OutputStream; import java.nio.charset.StandardCharsets; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.demo.utils.JAXBUtils; diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/Test.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/Test.java index 0afa62a6434..0369d576073 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/Test.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/Test.java @@ -35,4 +35,6 @@ public interface Test { String addString(String[] strArr); String testTraceId(); + + int[] testIntArray(int[] request); } diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/utils/JAXBUtils.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/utils/JAXBUtils.java index 671260f049f..7ff2518e8a3 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/utils/JAXBUtils.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/utils/JAXBUtils.java @@ -20,9 +20,6 @@ import java.io.StringWriter; import java.nio.charset.StandardCharsets; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.Marshaller; -import javax.xml.bind.Unmarshaller; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.Source; import javax.xml.transform.sax.SAXSource; @@ -33,6 +30,10 @@ import com.fasterxml.jackson.databind.JavaType; +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.Marshaller; +import jakarta.xml.bind.Unmarshaller; + public class JAXBUtils { private static final Logger LOGGER = LoggerFactory.getLogger(JAXBUtils.class); @@ -55,7 +56,7 @@ public static String convertToXml(Object obj, String encoding) { marshaller.marshal(obj, writer); result = writer.toString(); } catch (Exception e) { - LOGGER.error("Bean convert to xml failed, error message: {}", e.getMessage()); + LOGGER.error("Bean convert to xml failed, error message: {}", e.getMessage(), e); } return result; } diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/validator/Student.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/validator/Student.java index 65feea2ec7f..3117c5e1f82 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/validator/Student.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/validator/Student.java @@ -17,8 +17,8 @@ package org.apache.servicecomb.demo.validator; -import javax.validation.constraints.Max; -import javax.validation.constraints.NotNull; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.NotNull; public class Student { @NotNull diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/validator/Teacher.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/validator/Teacher.java index 009a25030b4..b7015e2920e 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/validator/Teacher.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/validator/Teacher.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.demo.validator; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotBlank; public class Teacher { diff --git a/demo/demo-schema/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor b/demo/demo-schema/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor index ff9a6cb9a54..acd03890620 100644 --- a/demo/demo-schema/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor +++ b/demo/demo-schema/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor @@ -16,4 +16,4 @@ # org.apache.servicecomb.demo.produceprocessor.ProduceAppXmlProcessor -org.apache.servicecomb.demo.produceprocessor.override.ProduceAppXmlProcessor \ No newline at end of file +org.apache.servicecomb.demo.produceprocessor.override.ProduceAppXmlProcessor diff --git a/demo/demo-schema/src/main/resources/microservice.yaml b/demo/demo-schema/src/main/resources/microservice.yaml index 684d88dd6ab..28df63a4323 100644 --- a/demo/demo-schema/src/main/resources/microservice.yaml +++ b/demo/demo-schema/src/main/resources/microservice.yaml @@ -20,3 +20,10 @@ demo.multi.service.center.serverB: key1: key1 key2: key2 key3: key3 + +servicecomb: + # verbose exceptions information + invocation: + exception: + print-stack-trace: true + print-rate-limit: true diff --git a/demo/demo-schema/src/main/resources/microservices/jaxrs/compute.yaml b/demo/demo-schema/src/main/resources/microservices/jaxrs/compute.yaml deleted file mode 100644 index 6606be573ae..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/jaxrs/compute.yaml +++ /dev/null @@ -1,201 +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. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: rest test - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.compute.Compute - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /compute -produces: - - application/json - -paths: - /add: - post: - operationId: add - parameters: - - name: a - in: formData - required: true - type: integer - format: int32 - - name: b - in: formData - required: true - type: integer - format: int32 - responses: - "200": - description: add numer - schema: - type: integer - format: int32 - /reduce: - get: - operationId: reduce - parameters: - - name: a - in: query - required: true - type: integer - format: int32 - - name: b - in: query - required: true - type: integer - format: int32 - responses: - "200": - description: reduce - schema: - type: integer - format: int32 - /sayhello: - post: - operationId: sayHello - parameters: - - name: user - in: body - required: true - schema: - $ref: '#/definitions/Person' - responses: - "200": - description: say hello - schema: - $ref: '#/definitions/Person' - /testrawjson: - post: - operationId: testRawJsonString - parameters: - - name: jsonInput - in: body - required: true - x-raw-json: true - schema: - type: string - responses: - "200": - description: test RawJson String - schema: - type: string - /saysomething: - post: - operationId: saySomething - parameters: - - name: prefix - in: header - required: true - type: string - - name: user - in: body - required: true - schema: - $ref: '#/definitions/Person' - responses: - "200": - description: say something - schema: - type: string - /sayhi/{name}: - put: - operationId: sayHi - parameters: - - name: name - in: path - required: true - type: string - responses: - "200": - description: say hi - schema: - type: string - 202: - description: say hi - schema: - type: string - /sayhi/{name}/v2: - put: - operationId: sayHi2 - parameters: - - name: name - in: path - required: true - type: string - responses: - "200": - description: say hi - schema: - type: string - /sayhei: - delete: - operationId: sayHei - parameters: - - name: name - in: query - required: true - type: string - responses: - "200": - description: say hi - schema: - type: string - /istrue: - get: - operationId: isTrue - responses: - "200": - description: is true - schema: - type: boolean - /addstring: - delete: - operationId: addString - produces: - - text/plain - parameters: - - name: s - in: query - required: true - type: array - items: - type: string - responses: - "200": - description: add string - schema: - type: string - -definitions: - Person: - type: object - properties: - name: - type: string - x-java-class: "org.apache.servicecomb.demo.compute.Person" diff --git a/demo/demo-schema/src/main/resources/microservices/no-use/cse.basic.Basic/main.idl b/demo/demo-schema/src/main/resources/microservices/no-use/cse.basic.Basic/main.idl deleted file mode 100644 index 6b5cd765367..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/no-use/cse.basic.Basic/main.idl +++ /dev/null @@ -1,62 +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 cse.basic; - -message BasicRequest { - required AllBasicTypes request = 1; -} - -message BasicResponse { - required AllBasicTypes request = 1; -} - -message AllBasicTypes { - required double tdouble = 1; - required float tfloat = 2; - required int32 tint32 = 3; - required int64 tint64 = 4; - required uint32 tuint32 = 5; - required uint64 tuint64 = 6; - required sint32 tsint32 = 7; - required sint64 tsint64 = 8; - required fixed32 tfixed32 = 9; - required fixed64 tfixed64 = 10; - required sfixed32 tsfixed32 = 11; - required sfixed64 tsfixed64 = 12; - required bool tbool = 13; - required string tstring = 14; - required bytes tbytes = 15; - - enum EnumType { - ONE = 0; - TWO = 1; - } - required EnumType enumType = 16; - - // map mapType = 17; - //message MapFieldEntry { - //required string key = 1; - //required string value = 2; - //} - //repeated MapFieldEntry mapType = 17; - - repeated string tstringList = 17; -} - -service Basic { - rpc schalarType(BasicRequest) returns(BasicResponse); -} diff --git a/demo/demo-schema/src/main/resources/microservices/no-use/hello/main.idl b/demo/demo-schema/src/main/resources/microservices/no-use/hello/main.idl deleted file mode 100644 index 6a6fd47fd1a..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/no-use/hello/main.idl +++ /dev/null @@ -1,26 +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. - -service Greeter { - rpc SayHello (HelloRequest) returns (HelloReply) {} -} - -message HelloRequest { - required string name = 1; -} - -message HelloReply { - required string message = 1; -} diff --git a/demo/demo-schema/src/main/resources/microservices/no-use/helloworld.Greeter/main.idl b/demo/demo-schema/src/main/resources/microservices/no-use/helloworld.Greeter/main.idl deleted file mode 100644 index e415db0350e..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/no-use/helloworld.Greeter/main.idl +++ /dev/null @@ -1,29 +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 helloworld; - -service Greeter { - rpc SayHello (HelloRequest) returns (HelloReply); - rpc SayHelloAgain (HelloRequest) returns (HelloReply); -} - -message HelloRequest { - required string name = 1; -} - -message HelloReply { - required string message = 1; -} diff --git a/demo/demo-schema/src/main/resources/microservices/no-use/helloworld.Standard/main.idl b/demo/demo-schema/src/main/resources/microservices/no-use/helloworld.Standard/main.idl deleted file mode 100644 index 2cf0a7a738a..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/no-use/helloworld.Standard/main.idl +++ /dev/null @@ -1,34 +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 helloworld; - -message StandardRequest { - required ComplexType args0 = 1; -} - -message StandardResponse { - required ComplexType response = 1; -} - -message ComplexType { - repeated int32 intType = 1; - repeated bool boolType = 2; - repeated string stringType = 3; -} - -service Standard { - rpc sayHelloComplex (StandardRequest) returns (StandardResponse); -} diff --git a/demo/demo-schema/src/main/resources/microservices/no-use/server/main.idl b/demo/demo-schema/src/main/resources/microservices/no-use/server/main.idl deleted file mode 100644 index d60a7a86798..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/no-use/server/main.idl +++ /dev/null @@ -1,97 +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. - -message TestRequest{ - required int32 index = 1; - required User user = 2; - required bytes data = 3; - //repeated MapFieldEntry friends = 4; -} - -message User{ - required string name = 1; - required int32 age = 2; - required int32 index = 3; - //repeated MapFieldEntry friends = 4; -} - -message MapFieldEntry { - required string key = 1; - required int32 value = 2; -} - -message TestResponse{ - required User user = 1; -} - -enum EnumType { - ONE = 0; - TWO = 1; -} - -message EnumMessage{ - required EnumType enumType = 1; -} - -message Empty{ -} - -message MicroService{ - optional string service_id = 1; - required string app_id = 2; - required string name = 3; - required string version = 4; - optional string description = 5; - optional string level = 6 [default = "front"]; - repeated ServiceDefinition definitions = 7; - optional ServiceStatus status = 8 [default = INSERVICE]; -} - -message ServiceDefinition { - required string name = 1; - required string version = 2; - required string transport = 3; - repeated string schema = 4; - //map properties = 5; -} - -enum ServiceStatus { - INSERVICE = 0; - OUTOFSERVICE = 1; -} - -message MicroServiceWrap{ - required MicroService service = 1; -} - -service server { - rpc splitParam(TestRequest) returns(User); - rpc standard(TestRequest) returns(TestResponse); - rpc sayHello (HelloRequest) returns (HelloReply); - rpc standardSayHello (HelloRequest) returns (HelloReply); - rpc testEnum(EnumMessage) returns(EnumMessage); - rpc testStandardEnum(EnumMessage) returns(EnumMessage); - rpc testEmpty(Empty) returns(Empty); - rpc testMicroService(MicroService) returns(MicroService); - rpc testCreateMicroService(MicroServiceWrap) returns(MicroServiceWrap); -} - -message HelloRequest { - required string name = 1; -} - -message HelloReply { - required string message = 1; -} diff --git a/demo/demo-schema/src/main/resources/microservices/pojo/helloworld.Greeter.yaml b/demo/demo-schema/src/main/resources/microservices/pojo/helloworld.Greeter.yaml deleted file mode 100644 index d503f154ce0..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/pojo/helloworld.Greeter.yaml +++ /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. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: helloworld.Greeter - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.helloworld.greeter.Hello - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /pojo/rest/helloworld.Greeter -produces: - - application/json - -paths: - /SayHello: - post: - operationId: SayHello - parameters: - - name: name - in: body - required: true - schema: - type: string - responses: - "200": - description: asdf - schema: - type: string - default: - description: asf - schema: - type: string - /SayHelloAgain: - post: - operationId: SayHelloAgain - parameters: - - name: name - in: body - required: true - schema: - type: string - responses: - "200": - description: asdf - schema: - type: string - default: - description: asf - schema: - type: string diff --git a/demo/demo-schema/src/main/resources/microservices/pojo/server.yaml b/demo/demo-schema/src/main/resources/microservices/pojo/server.yaml deleted file mode 100644 index 9c8be9e8400..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/pojo/server.yaml +++ /dev/null @@ -1,221 +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. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: cse test - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.server.Test - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /pojo/rest -produces: - - application/json - -paths: - /testStringArray: - get: - operationId: testStringArray - parameters: - - name: arr - in: query - required: true - type: array - items: - type: string - responses: - "200": - description: asdf - schema: - type: string - /testStatic: - get: - operationId: getTestString - parameters: - - name: code - in: query - required: false - type: string - responses: - "200": - description: asdf - schema: - type: string - post: - operationId: postTestStatic - parameters: - - name: code - in: query - required: true - type: integer - format: int32 - responses: - "200": - description: asdf - schema: - type: string - patch: - operationId: patchTestStatic - parameters: - - name: code - in: query - required: true - type: integer - format: int32 - responses: - "200": - description: asdf - schema: - type: string - /testException/{code}: - get: - operationId: testException - parameters: - - name: code - in: path - required: true - type: integer - format: int32 - responses: - "200": - description: asdf - schema: - type: string - 456: - description: asdf - schema: - type: string - 556: - description: asdf - schema: - type: array - items: - type: string - 557: - description: asdf - schema: - type: array - items: - type: array - items: - type: string - /splitParam: - post: - operationId: splitParam - parameters: - - name: index - in: query - required: true - type: integer - format: int32 - - name: user - in: body - schema: - $ref: '#/definitions/User' - responses: - "200": - description: asdf - schema: - $ref: '#/definitions/User' - default: - description: asf - schema: - $ref: '#/definitions/Error' - /wrapParam: - post: - operationId: wrapParam - parameters: - - name: request - in: body - required: false - schema: - $ref: '#/definitions/TestRequest' - responses: - "200": - description: asdf - schema: - $ref: '#/definitions/User' - default: - description: asf - schema: - $ref: '#/definitions/Error' - /addstring: - delete: - operationId: addString - parameters: - - name: strArr - in: query - required: true - type: array - items: - type: string - responses: - "200": - description: add string - schema: - type: string - /testTraceId: - get: - operationId: testTraceId - responses: - "200": - description: get trace id - schema: - type: string -definitions: - Error: - type: object - x-java-class: org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData - properties: - message: - type: string - TestRequest: - type: object - properties: - index: - type: integer - format: int32 - user: - $ref: '#/definitions/User' - users: - type: array - items: - $ref: '#/definitions/User' - data: - type: string - format: byte - User: - type: object - properties: - name: - type: string - age: - type: integer - format: int32 - index: - type: integer - format: int32 - x-java-class: "org.apache.servicecomb.demo.server.User" diff --git a/demo/demo-schema/src/main/resources/microservices/pojo/smartcare.yaml b/demo/demo-schema/src/main/resources/microservices/pojo/smartcare.yaml deleted file mode 100644 index 0879c3bda2d..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/pojo/smartcare.yaml +++ /dev/null @@ -1,136 +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. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: smartcare test - version: 1.0.0 -# x-java-interface is optional, remove it should works well, but sometimes need to generate more class in memory -# x-java-interface: org.apache.servicecomb.demo.smartcare.SmartCare - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /pojo/rest/plat/meta/v1 -produces: - - application/json - -paths: - /applications: - post: - operationId: addApplication - parameters: - - name: application - in: body - required: true - schema: - $ref: '#/definitions/Application' - responses: - 201: - description: success - schema: - $ref: '#/definitions/Response' - 400: - description: error - schema: - $ref: '#/definitions/Response' - 500: - description: error - schema: - $ref: '#/definitions/Response' - - /applications/{appName}: - delete: - operationId: delApplication - parameters: - - name: appName - in: path - required: true - type: string - responses: - 201: - description: success - schema: - $ref: '#/definitions/Response' - 400: - description: error - schema: - $ref: '#/definitions/Response' - 500: - description: error - schema: - $ref: '#/definitions/Response' - - -definitions: - Application: - type: object - properties: - name: - type: string - labelEN: - type: string - labelCH: - type: string - defaultGroup: - type: string - version: - type: string - dynamicFlag: - type: boolean - groups: - type: array - items: - $ref: '#/definitions/Group' - required: - - name - - defaultGroup - - version - - dynamicFlag - - groups - - Group: - type: object - properties: - name: - type: string - labelEN: - type: string - labelCH: - type: string - required: - - name - - Response: - type: object - x-java-class: org.apache.servicecomb.demo.smartcare.Response - properties: - resultCode: - type: integer - format: int32 - resultMessage: - type: string - required: - - resultCode - diff --git a/demo/demo-schema/src/main/resources/microservices/pojo/tcc-server.yaml b/demo/demo-schema/src/main/resources/microservices/pojo/tcc-server.yaml deleted file mode 100644 index 4af831b88d8..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/pojo/tcc-server.yaml +++ /dev/null @@ -1,108 +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. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: cse test - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.tccserver.TestTcc - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /pojo/rest/tcc-server -produces: - - application/json - -paths: - /splitParam: - post: - operationId: splitParam - parameters: - - name: index - in: query - required: true - type: number - - name: user - in: body - schema: - $ref: '#/definitions/User' - responses: - "200": - description: asdf - schema: - $ref: '#/definitions/User' - default: - description: asf - schema: - $ref: '#/definitions/Error' - /wrapParam: - post: - operationId: wrapParam - parameters: - - name: TestRequest - in: body - required: true - schema: - $ref: '#/definitions/TestRequest' - responses: - "200": - description: asdf - schema: - $ref: '#/definitions/User' - default: - description: asf - schema: - $ref: '#/definitions/Error' - -definitions: - Error: - type: object - x-java-class: org.apache.servicecomb.core.exception.CommonExceptionData - properties: - message: - type: string - TestRequest: - type: object - properties: - index: - type: number - user: - $ref: '#/definitions/User' - users: - type: array - items: - $ref: '#/definitions/User' - data: - type: string - format: byte - User: - type: object - properties: - name: - type: string - age: - type: number - index: - type: number diff --git a/demo/demo-schema/src/main/resources/microservices/springmvc/controller.yaml b/demo/demo-schema/src/main/resources/microservices/springmvc/controller.yaml deleted file mode 100644 index be2feb0bfca..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/springmvc/controller.yaml +++ /dev/null @@ -1,136 +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. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: rest test - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.controller.Controller - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /springmvc/controller -produces: - - application/json - -paths: - /add: - get: - operationId: add - parameters: - - name: a - in: query - required: true - type: integer - format: int32 - - name: b - in: query - required: true - type: integer - format: int32 - responses: - "200": - description: add numer - schema: - type: integer - format: int32 - - /sayhello/{name}: - post: - operationId: sayHello - parameters: - - name: name - in: path - required: true - type: string - responses: - "200": - description: say hello - schema: - type: string - /saysomething: - post: - operationId: saySomething - parameters: - - name: prefix - in: query - required: true - type: string - - name: user - in: body - required: true - schema: - $ref: '#/definitions/Person' - responses: - "200": - description: say something - schema: - type: string - /sayhi: - get: - operationId: sayHi - parameters: - - name: name - in: query - required: true - type: string - responses: - "200": - description: say hi - schema: - type: string - /sayhei: - get: - operationId: sayHei - parameters: - - name: name - in: header - required: true - type: string - responses: - "200": - description: say hei - schema: - type: string - /sayHello1: - get: - operationId: sayHello1 - parameters: - - name: name - in: query - required: true - type: string - responses: - "200": - description: check the handler is effective - schema: - type: string - -definitions: - Person: - type: object - properties: - name: - type: string diff --git a/demo/demo-signature/pom.xml b/demo/demo-signature/pom.xml deleted file mode 100644 index e4e35f3f895..00000000000 --- a/demo/demo-signature/pom.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - 4.0.0 - - org.apache.servicecomb.demo - demo-parent - 3.0.0-SNAPSHOT - - demo-signature - Java Chassis::Demo::Signature - - - - org.apache.servicecomb - common-rest - - - org.apache.servicecomb - registry-service-center - - - diff --git a/demo/demo-signature/src/main/java/org/apache/servicecomb/demo/signature/ClientSignature.java b/demo/demo-signature/src/main/java/org/apache/servicecomb/demo/signature/ClientSignature.java deleted file mode 100644 index 84bbe671340..00000000000 --- a/demo/demo-signature/src/main/java/org/apache/servicecomb/demo/signature/ClientSignature.java +++ /dev/null @@ -1,61 +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.servicecomb.demo.signature; - -import java.util.concurrent.CompletableFuture; - -import javax.ws.rs.core.Response.Status; - -import org.apache.servicecomb.common.rest.filter.HttpClientFilter; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; -import org.apache.servicecomb.swagger.invocation.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ClientSignature implements HttpClientFilter { - private static final Logger LOGGER = LoggerFactory.getLogger(ClientSignature.class); - - @Override - public int getOrder() { - return 0; - } - - @Override - public CompletableFuture beforeSendRequestAsync(Invocation invocation, HttpServletRequestEx requestEx) { - String signature = SignatureUtils.genSignature(requestEx); - requestEx.setHeader("signature", signature); - return CompletableFuture.completedFuture(null); - } - - @Override - public Response afterReceiveResponse(Invocation invocation, HttpServletResponseEx responseEx) { - String signature = SignatureUtils.genSignature(responseEx); - String serverSignature = responseEx.getHeader("signature"); - - if (serverSignature != null) { - LOGGER.debug("check response signature, client: {}, server: {}.", signature, serverSignature); - if (!signature.equals(serverSignature)) { - LOGGER.error("check response signature failed"); - return Response.create(Status.UNAUTHORIZED, "check response signature failed"); - } - } - return null; - } -} diff --git a/demo/demo-signature/src/main/java/org/apache/servicecomb/demo/signature/ServerSignature.java b/demo/demo-signature/src/main/java/org/apache/servicecomb/demo/signature/ServerSignature.java deleted file mode 100644 index b39b603717c..00000000000 --- a/demo/demo-signature/src/main/java/org/apache/servicecomb/demo/signature/ServerSignature.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.servicecomb.demo.signature; - -import java.util.concurrent.CompletableFuture; - -import org.apache.servicecomb.common.rest.filter.HttpServerFilter; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.definition.OperationMeta; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; -import org.apache.servicecomb.swagger.invocation.Response; - -public class ServerSignature implements HttpServerFilter { - public ServerSignature() { - } - - @Override - public int getOrder() { - return 0; - } - - @Override - public boolean needCacheRequest(OperationMeta operationMeta) { - return true; - } - - @Override - public Response afterReceiveRequest(Invocation invocation, HttpServletRequestEx requestEx) { - return null; - } - - @Override - public CompletableFuture beforeSendResponseAsync(Invocation invocation, HttpServletResponseEx responseEx) { - String signature = SignatureUtils.genSignature(responseEx); - responseEx.addHeader("signature", signature); - return CompletableFuture.completedFuture(null); - } -} diff --git a/demo/demo-signature/src/main/java/org/apache/servicecomb/demo/signature/SignatureUtils.java b/demo/demo-signature/src/main/java/org/apache/servicecomb/demo/signature/SignatureUtils.java deleted file mode 100644 index 33e485b1f8d..00000000000 --- a/demo/demo-signature/src/main/java/org/apache/servicecomb/demo/signature/SignatureUtils.java +++ /dev/null @@ -1,80 +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.servicecomb.demo.signature; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; - -import javax.ws.rs.core.MediaType; - -import org.apache.commons.lang.StringUtils; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; - -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; -import com.netflix.config.DynamicStringListProperty; - -public class SignatureUtils { - private static final DynamicStringListProperty PARAM_NAMES_PROPERTY = - new DynamicStringListProperty("servicecomb.demo.signature.param-names", Arrays.asList("userId")); - - private static List paramNames = PARAM_NAMES_PROPERTY.get(); - - static { - PARAM_NAMES_PROPERTY.addCallback(() -> { - List tmpNames = PARAM_NAMES_PROPERTY.get(); - tmpNames.sort(Comparator.naturalOrder()); - paramNames = tmpNames; - }); - } - - public static String genSignature(HttpServletRequestEx requestEx) { - Hasher hasher = Hashing.sha256().newHasher(); - hasher.putString(requestEx.getRequestURI(), StandardCharsets.UTF_8); - for (String paramName : paramNames) { - String paramValue = requestEx.getHeader(paramName); - if (paramValue != null) { - hasher.putString(paramName, StandardCharsets.UTF_8); - hasher.putString(paramValue, StandardCharsets.UTF_8); - System.out.printf("%s %s\n", paramName, paramValue); - } - } - - if (!StringUtils.startsWithIgnoreCase(requestEx.getContentType(), MediaType.APPLICATION_FORM_URLENCODED)) { - byte[] bytes = requestEx.getBodyBytes(); - if (bytes != null) { - hasher.putBytes(bytes, 0, requestEx.getBodyBytesLength()); - } - } - - return hasher.hash().toString(); - } - - public static String genSignature(HttpServletResponseEx responseEx) { - Hasher hasher = Hashing.sha256().newHasher(); - byte[] bytes = responseEx.getBodyBytes(); - if (bytes != null) { - hasher.putBytes(bytes, 0, responseEx.getBodyBytesLength()); - } - - return hasher.hash().toString(); - } -} diff --git a/demo/demo-signature/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpClientFilter b/demo/demo-signature/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpClientFilter deleted file mode 100644 index 9533c795685..00000000000 --- a/demo/demo-signature/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpClientFilter +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.demo.signature.ClientSignature diff --git a/demo/demo-signature/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter b/demo/demo-signature/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter deleted file mode 100644 index 37d7671228c..00000000000 --- a/demo/demo-signature/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.demo.signature.ServerSignature diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/pom.xml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/pom.xml index bcdb324aa71..372d3a8ccc3 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/pom.xml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-spring-boot-transport - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -39,24 +39,88 @@ demo-spring-boot-pojo-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/DemoConst.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/DemoConst.java index caf35fff229..eb60d1e9d83 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/DemoConst.java +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/DemoConst.java @@ -17,8 +17,8 @@ package org.apache.servicecomb.demo.springboot.pojo.client; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; public interface DemoConst { - String[] transports = new String[] {"rest", Const.ANY_TRANSPORT}; + String[] transports = new String[] {"rest", CoreConst.ANY_TRANSPORT}; } diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClient.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClient.java index 11f32a9f5d3..b5a590b65e7 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClient.java +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClient.java @@ -17,10 +17,10 @@ package org.apache.servicecomb.demo.springboot.pojo.client; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.annotation.ImportResource; /** * SpringmvcClient @@ -28,7 +28,7 @@ * */ @SpringBootApplication -@EnableServiceComb +@ImportResource(value = "classpath*:META-INF/spring/*.bean.xml") public class PojoClient { public static void main(final String[] args) throws Exception { diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientTest.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientTest.java index d6af5b58a2a..4e0525a1230 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientTest.java +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientTest.java @@ -21,11 +21,11 @@ import java.util.HashMap; import java.util.Map; +import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.demo.springboot.pojo.server.schema.server.Test; import org.apache.servicecomb.demo.springboot.pojo.server.schema.server.TestRequest; import org.apache.servicecomb.demo.springboot.pojo.server.schema.server.User; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.apache.servicecomb.provider.pojo.RpcReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,10 +35,10 @@ public class PojoClientTest { private static Logger LOGGER = LoggerFactory.getLogger(PojoClientTest.class); - @RpcReference(microserviceName = "pojo", schemaId = "server") + @RpcReference(microserviceName = "spring-boot-pojo-server", schemaId = "server") public static Test test; - public static Test testFromXml; + private static Test testFromXml; public static final byte[] buffer = new byte[1024]; @@ -53,10 +53,10 @@ public static void setTestFromXml(Test testFromXml) { } public static void runTest() throws Exception { - String microserviceName = "pojo"; + String microserviceName = "spring-boot-pojo-server"; for (String transport : DemoConst.transports) { - ArchaiusUtils.setProperty("servicecomb.references.transport." + microserviceName, transport); + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); LOGGER.info("test {}, transport {}", microserviceName, transport); @@ -92,23 +92,26 @@ private static void testSplitParam(Test test) { @SuppressWarnings({"deprecation"}) private static void testCommonInvoke(String transport) { Map arguments = new HashMap<>(); + Map wrap = new HashMap<>(); arguments.put("index", 2); arguments.put("user", new User()); + wrap.put("splitParamBody", arguments); - Object result = InvokerUtils.syncInvoke("pojo", "server", "splitParam", arguments, User.class); + Object result = InvokerUtils.syncInvoke("spring-boot-pojo-server", "server", "splitParam", wrap, User.class); TestMgr.check("User [name=nameA, users count:0" + SPLITPARAM_RESPONSE_USER_SUFFIX + ", age=100, index=2]", result); arguments = new HashMap<>(); arguments.put("index", 3); arguments.put("user", new User()); + wrap = new HashMap<>(); + wrap.put("splitParamBody", arguments); result = - InvokerUtils.syncInvoke("pojo", - "0.0.1", + InvokerUtils.syncInvoke("spring-boot-pojo-server", transport, "server", "splitParam", - arguments, User.class); + wrap, User.class); TestMgr.check("User [name=nameA, users count:0" + SPLITPARAM_RESPONSE_USER_SUFFIX + ", age=100, index=3]", result); } diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/META-INF/spring/pojo.client.bean.xml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/META-INF/spring/pojo.client.bean.xml index 5f6e1d31b9d..a2219f31ba4 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/META-INF/spring/pojo.client.bean.xml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/META-INF/spring/pojo.client.bean.xml @@ -22,7 +22,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.huawei.com/schema/paas/cse/rpc classpath:META-INF/spring/spring-paas-cse-rpc.xsd"> - diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/application.yml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/application.yml index 31528e354b9..1d901a66c99 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/application.yml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/application.yml @@ -18,15 +18,13 @@ server: port: 8082 -# java-chassis configurations - -APPLICATION_ID: spring-boot-pojotest -service_description: - name: pojoClient - version: 0.0.1 servicecomb: service: - registry: + application: spring-boot-pojotest + name: spring-boot-pojo-client + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 handler: chain: @@ -37,4 +35,4 @@ servicecomb: enabled: false references: pojo: - version-rule: 0.0.1 \ No newline at end of file + version-rule: 0.0.1 diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/pom.xml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/pom.xml index 99ba3201f2b..60761704af9 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/pom.xml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-spring-boot-transport - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -37,10 +37,6 @@ jakarta.ws.rs jakarta.ws.rs-api - - org.apache.servicecomb - inspector - @@ -56,28 +52,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/PojoServer.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/PojoServer.java index a068886ac13..30af50bf5f5 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/PojoServer.java +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/PojoServer.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.demo.springboot.pojo.server; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class PojoServer { public static void main(final String[] args) throws Exception { diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/handler/MyHandler.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/handler/MyHandler.java index 1be19128a6b..09d2e2eb06d 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/handler/MyHandler.java +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/handler/MyHandler.java @@ -19,32 +19,29 @@ import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.filter.ProducerFilter; +import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.demo.springboot.pojo.server.schema.server.User; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component -public class MyHandler implements ProducerFilter { +public class MyHandler extends AbstractFilter implements ProviderFilter { private static final Logger LOGGER = LoggerFactory.getLogger(MyHandler.class); public static final String SPLITPARAM_RESPONSE_USER_SUFFIX = "(modified by MyHandler)"; @Override - public int getOrder(InvocationType invocationType, String microservice) { - return Filter.PRODUCER_SCHEDULE_FILTER_ORDER - 100; + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 100; } - @Nonnull @Override public String getName() { return "test-my-filter"; diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/application.yml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/application.yml index 483260be63a..e7c0ccf1da0 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/application.yml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/application.yml @@ -18,15 +18,13 @@ server: port: 8080 -# java-chassis configurations - -APPLICATION_ID: spring-boot-pojotest -service_description: - name: pojo - version: 0.0.1 servicecomb: service: - registry: + application: spring-boot-pojotest + name: spring-boot-pojo-server + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/helloworld.Greeter.yaml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/helloworld.Greeter.yaml deleted file mode 100644 index d503f154ce0..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/helloworld.Greeter.yaml +++ /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. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: helloworld.Greeter - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.helloworld.greeter.Hello - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /pojo/rest/helloworld.Greeter -produces: - - application/json - -paths: - /SayHello: - post: - operationId: SayHello - parameters: - - name: name - in: body - required: true - schema: - type: string - responses: - "200": - description: asdf - schema: - type: string - default: - description: asf - schema: - type: string - /SayHelloAgain: - post: - operationId: SayHelloAgain - parameters: - - name: name - in: body - required: true - schema: - type: string - responses: - "200": - description: asdf - schema: - type: string - default: - description: asf - schema: - type: string diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/server.yaml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/server.yaml deleted file mode 100644 index 4717f8e483e..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/server.yaml +++ /dev/null @@ -1,197 +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. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: cse test - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.springboot.pojo.server.schema.server.Test - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /pojo/rest -produces: - - application/json - -paths: - /testStringArray: - get: - operationId: testStringArray - parameters: - - name: arr - in: query - required: true - type: array - items: - type: string - responses: - "200": - description: asdf - schema: - type: string - /testStatic: - get: - operationId: getTestString - parameters: - - name: code - in: query - required: false - type: string - responses: - "200": - description: asdf - schema: - type: string - post: - operationId: postTestStatic - parameters: - - name: code - in: query - required: true - type: integer - format: int32 - responses: - "200": - description: asdf - schema: - type: string - /testException/{code}: - get: - operationId: testException - parameters: - - name: code - in: path - required: true - type: integer - format: int32 - responses: - "200": - description: asdf - schema: - type: string - 456: - description: asdf - schema: - type: string - 556: - description: asdf - schema: - type: array - items: - type: string - 557: - description: asdf - schema: - type: array - items: - type: array - items: - type: string - /splitParam: - post: - operationId: splitParam - parameters: - - name: index - in: query - required: true - type: number - - name: user - in: body - schema: - $ref: '#/definitions/User' - responses: - "200": - description: asdf - schema: - $ref: '#/definitions/User' - default: - description: asf - schema: - $ref: '#/definitions/Error' - /wrapParam: - post: - operationId: wrapParam - parameters: - - name: request - in: body - required: false - schema: - $ref: '#/definitions/TestRequest' - responses: - "200": - description: asdf - schema: - $ref: '#/definitions/User' - default: - description: asf - schema: - $ref: '#/definitions/Error' - /addstring: - delete: - operationId: addString - parameters: - - name: strArr - in: query - required: true - type: array - items: - type: string - responses: - "200": - description: add string - schema: - type: string - -definitions: - Error: - type: object - x-java-class: org.apache.servicecomb.core.exception.CommonExceptionData - properties: - message: - type: string - TestRequest: - type: object - properties: - index: - type: number - user: - $ref: '#/definitions/User' - users: - type: array - items: - $ref: '#/definitions/User' - data: - type: string - format: byte - User: - type: object - properties: - name: - type: string - age: - type: number - index: - type: number - x-java-class: "org.apache.servicecomb.demo.springboot.pojo.server.schema.server.User" \ No newline at end of file diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/smartcare.yaml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/smartcare.yaml deleted file mode 100644 index 49a8ac93e72..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/smartcare.yaml +++ /dev/null @@ -1,135 +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. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: smartcare test - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.smartcare.SmartCare - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /pojo/rest/plat/meta/v1 -produces: - - application/json - -paths: - /applications: - post: - operationId: addApplication - parameters: - - name: application - in: body - required: true - schema: - $ref: '#/definitions/Application' - responses: - 201: - description: success - schema: - $ref: '#/definitions/Response' - 400: - description: error - schema: - $ref: '#/definitions/Response' - 500: - description: error - schema: - $ref: '#/definitions/Response' - - /applications/{appName}: - delete: - operationId: delApplication - parameters: - - name: appName - in: path - required: true - type: string - responses: - 201: - description: success - schema: - $ref: '#/definitions/Response' - 400: - description: error - schema: - $ref: '#/definitions/Response' - 500: - description: error - schema: - $ref: '#/definitions/Response' - - -definitions: - Application: - type: object - properties: - name: - type: string - labelEN: - type: string - labelCH: - type: string - defaultGroup: - type: string - version: - type: string - dynamicFlag: - type: boolean - groups: - type: array - items: - $ref: '#/definitions/Group' - required: - - name - - defaultGroup - - version - - dynamicFlag - - groups - - Group: - type: object - properties: - name: - type: string - labelEN: - type: string - labelCH: - type: string - required: - - name - - Response: - type: object - x-java-class: org.apache.servicecomb.demo.smartcare.Response - properties: - resultCode: - type: integer - format: int32 - resultMessage: - type: string - required: - - resultCode - diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/tcc-server.yaml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/tcc-server.yaml deleted file mode 100644 index 4af831b88d8..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/tcc-server.yaml +++ /dev/null @@ -1,108 +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. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: cse test - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.tccserver.TestTcc - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /pojo/rest/tcc-server -produces: - - application/json - -paths: - /splitParam: - post: - operationId: splitParam - parameters: - - name: index - in: query - required: true - type: number - - name: user - in: body - schema: - $ref: '#/definitions/User' - responses: - "200": - description: asdf - schema: - $ref: '#/definitions/User' - default: - description: asf - schema: - $ref: '#/definitions/Error' - /wrapParam: - post: - operationId: wrapParam - parameters: - - name: TestRequest - in: body - required: true - schema: - $ref: '#/definitions/TestRequest' - responses: - "200": - description: asdf - schema: - $ref: '#/definitions/User' - default: - description: asf - schema: - $ref: '#/definitions/Error' - -definitions: - Error: - type: object - x-java-class: org.apache.servicecomb.core.exception.CommonExceptionData - properties: - message: - type: string - TestRequest: - type: object - properties: - index: - type: number - user: - $ref: '#/definitions/User' - users: - type: array - items: - $ref: '#/definitions/User' - data: - type: string - format: byte - User: - type: object - properties: - name: - type: string - age: - type: number - index: - type: number diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/pom.xml b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/pom.xml index 353e81bf9b1..edd73c1428b 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/pom.xml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/pom.xml @@ -26,7 +26,7 @@ org.apache.servicecomb.demo demo-spring-boot-transport - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -42,24 +42,88 @@ demo-spring-boot-springmvc-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/PlaceHolderSchemaTest.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/PlaceHolderSchemaTest.java index 7a70afa94a6..f7e066497d1 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/PlaceHolderSchemaTest.java +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/PlaceHolderSchemaTest.java @@ -21,13 +21,13 @@ import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class PlaceHolderSchemaTest implements CategorizedTestCase { @Override public void testRestTransport() throws Exception { - RestTemplate template = RestTemplateBuilder.create(); + RestOperations template = RestTemplateBuilder.create(); String result = template.getForObject("servicecomb://springmvc/placeholder/schema?name=test", String.class); TestMgr.check("test", result); } diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ReactiveStreamIT.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ReactiveStreamIT.java new file mode 100644 index 00000000000..c9c1957ddf5 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ReactiveStreamIT.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.springboot.springmvc.client; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.springboot.springmvc.client.ThirdSvcConfiguration.ReactiveStreamClient; +import org.apache.servicecomb.springboot.springmvc.client.ThirdSvcConfiguration.ReactiveStreamClient.Model; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component +public class ReactiveStreamIT implements CategorizedTestCase { + @Autowired + @Qualifier("reactiveStreamProvider") + ReactiveStreamClient reactiveStreamProvider; + + @Override + public void testRestTransport() throws Exception { + testSseString(reactiveStreamProvider); + testSseModel(reactiveStreamProvider); + } + + private void testSseModel(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseModel(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(Model s) { + buffer.append(s.getName()).append(s.getAge()); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("jack0jack1jack2jack3jack4", buffer.toString()); + } + + private void testSseString(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseString(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(String s) { + buffer.append(s); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("abc", buffer.toString()); + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/SpringMvcClient.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/SpringMvcClient.java index 3beb979f6f6..8ce77d9818e 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/SpringMvcClient.java +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/SpringMvcClient.java @@ -19,7 +19,6 @@ import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @@ -30,7 +29,6 @@ * */ @SpringBootApplication -@EnableServiceComb public class SpringMvcClient { public static void main(final String[] args) throws Exception { diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/TestAnnotationsSchema.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/TestAnnotationsSchema.java new file mode 100644 index 00000000000..7d77fa1be35 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/TestAnnotationsSchema.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.springboot.springmvc.client; + +import org.apache.http.HttpStatus; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.controller.Person; +import org.apache.servicecomb.provider.springmvc.reference.CseRestTemplate; +import org.apache.servicecomb.provider.springmvc.reference.UrlWithServiceNameClientHttpRequestFactory; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +@Component +public class TestAnnotationsSchema implements CategorizedTestCase { + private static final String microserviceName = "springmvc"; + + private static final RestTemplate templateUrlWithServiceName = new CseRestTemplate(); + + @Override + public void testRestTransport() throws Exception { + templateUrlWithServiceName.setRequestFactory(new UrlWithServiceNameClientHttpRequestFactory()); + testRequiredBody(templateUrlWithServiceName, microserviceName); + testRegExpPath(); + } + + private void testRegExpPath() { + String prefix = "cse://" + microserviceName; + String result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a?name={name}", + String.class, "a"); + TestMgr.check("a", result); + result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a/b?name={name}", + String.class, "ab"); + TestMgr.check("ab", result); + result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a/b/c?name={name}", + String.class, "abc"); + TestMgr.check("abc", result); + } + + private static void testRequiredBody(RestTemplate template, String microserviceName) { + String prefix = "cse://" + microserviceName; + Person user = new Person(); + + TestMgr.check("No user data found", + template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", + user, + String.class, + "ha")); + + user.setName("world"); + TestMgr.check("ha world", + template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", + user, + String.class, + "ha")); + + TestMgr.check("No user data found", + template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", + null, + String.class, + "ha")); + + TestMgr.check("No user name found", + template.postForObject(prefix + "/annotations/say", + "", + String.class, + "ha")); + TestMgr.check("test", + template.postForObject(prefix + "/annotations/say", + "test", + String.class, + "ha")); + + try { + template.postForObject(prefix + "/annotations/testRequiredBody", + null, + String.class); + TestMgr.fail("should fail"); + } catch (InvocationException e) { + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ThirdSvcConfiguration.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ThirdSvcConfiguration.java new file mode 100644 index 00000000000..098a36828ed --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ThirdSvcConfiguration.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.servicecomb.springboot.springmvc.client; + +import org.apache.servicecomb.provider.pojo.Invoker; +import org.reactivestreams.Publisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@Configuration +public class ThirdSvcConfiguration { + @RequestMapping(path = "/") + public interface ReactiveStreamClient { + class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + Publisher sseString(); + + @GetMapping("/sseModel") + Publisher sseModel(); + } + + @Bean("reactiveStreamProvider") + public ReactiveStreamClient reactiveStreamProvider() { + return Invoker.createProxy("springmvc", "ReactiveStreamController", ReactiveStreamClient.class); + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/UploadDownloadSchemaTest.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/UploadDownloadSchemaTest.java new file mode 100644 index 00000000000..18054cf4f4a --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/UploadDownloadSchemaTest.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.springboot.springmvc.client; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; + +@Component +public class UploadDownloadSchemaTest implements CategorizedTestCase { + RestOperations restOperations = RestTemplateBuilder.create(); + + @Override + public void testRestTransport() throws Exception { + testEmptyFileUploadWork(); + testNonEmptyFileUploadWork(); + } + + private void testNonEmptyFileUploadWork() throws Exception { + String file2Content = " bonjour"; + File someFile = File.createTempFile("upload2", ".txt"); + FileUtils.writeStringToFile(someFile, file2Content, StandardCharsets.UTF_8, false); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + Map params = new HashMap<>(); + params.put("name", "test"); + params.put("file", someFile); + HttpEntity> entity = new HttpEntity<>(params, headers); + String url = "servicecomb://springmvc/up/down/upload"; + String result = restOperations.postForObject(url, entity, String.class); + TestMgr.check("test; bonjour", result); + } + + private void testEmptyFileUploadWork() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + Map params = new HashMap<>(); + params.put("name", "test"); + HttpEntity> entity = new HttpEntity<>(params, headers); + String url = "servicecomb://springmvc/up/down/upload"; + String result = restOperations.postForObject(url, entity, String.class); + TestMgr.check("test;", result); + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/resources/application.yml b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/resources/application.yml index 1142e8e8748..68ab69a48cb 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/resources/application.yml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/resources/application.yml @@ -15,15 +15,16 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: springmvcboottest - -service_description: - name: springmvcclient - version: 0.0.3 - server: port: 8999 servicecomb: + service: + application: springmvcboottest + name: springmvcclient + version: 0.0.3 + registry: + sc: + address: http://127.0.0.1:30100 test: vert: transport: false diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/pom.xml b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/pom.xml index 490a61bf8c1..efa0723b59a 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/pom.xml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/pom.xml @@ -26,7 +26,7 @@ org.apache.servicecomb.demo demo-spring-boot-transport - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -47,28 +47,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/AnnotationsSchema.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/AnnotationsSchema.java new file mode 100644 index 00000000000..b1bcaba83ed --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/AnnotationsSchema.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.springboot.springmvc.server; + +import org.apache.servicecomb.demo.controller.Person; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.GetMapping; +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 io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.ws.rs.core.MediaType; + +@RestSchema(schemaId = "annotations") +@RequestMapping(path = "/springmvc/annotations", produces = MediaType.APPLICATION_JSON) +public class AnnotationsSchema { + @GetMapping(path = "/add") + public int add(@RequestParam(name = "a", defaultValue = "10") int a, + @RequestParam(name = "b", defaultValue = "10") int b) { + return a + b; + } + + @RequestMapping(path = "/sayhei", method = RequestMethod.GET) + public String sayHei(@RequestHeader(name = "name", defaultValue = "test") String name) { + return "hei " + name; + } + + @GetMapping(path = "/sayhi") + @Parameters({ + @Parameter(name = "name", in = ParameterIn.QUERY, schema = @Schema(type = "string", defaultValue = "test")), + @Parameter(name = "age", in = ParameterIn.QUERY, schema = @Schema(type = "integer", defaultValue = "20")) + }) + public String sayHi(String name, int age) { + return "hi " + name + " your age is : " + age; + } + + @RequestMapping(path = "/saysomething", method = RequestMethod.POST) + public String saySomething(String prefix, @RequestBody(required = false) Person user) { + if (user == null || user.getName() == null || user.getName().isEmpty()) { + return "No user data found"; + } + return prefix + " " + user.getName(); + } + + @RequestMapping(path = "/say", method = RequestMethod.POST) + public String say(@RequestBody(required = false) String user) { + if (user == null || user.isEmpty()) { + return "No user name found"; + } + return user; + } + + @RequestMapping(path = "/testRequiredBody", method = RequestMethod.POST) + public String testRequiredBody(@RequestBody(required = true) Person user) { + if (user == null) { + return "Should not happen"; + } + return user.getName(); + } + + @RequestMapping(path = "/testRegExpPath/{path: .+}", method = RequestMethod.GET) + public String testRegExpPath(@RequestParam("name") String name) { + return name; + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/ReactiveStreamController.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/ReactiveStreamController.java new file mode 100644 index 00000000000..e82028d1286 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/ReactiveStreamController.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.servicecomb.springboot.springmvc.server; + + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.reactivex.rxjava3.core.Flowable; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +public class ReactiveStreamController { + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + public Publisher sseString() { + return Flowable.fromArray("a", "b", "c"); + } + + @GetMapping("/sseModel") + public Publisher sseModel() { + return Flowable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS) + .map(item -> new Model("jack", item.intValue())); + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/SpringmvcServer.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/SpringmvcServer.java index 6cbebf848ae..acbd39e0cc4 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/SpringmvcServer.java +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/SpringmvcServer.java @@ -18,7 +18,7 @@ package org.apache.servicecomb.springboot.springmvc.server; import org.apache.servicecomb.core.SCBEngine; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; @@ -26,10 +26,7 @@ import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; -import com.netflix.config.DynamicPropertyFactory; - @SpringBootApplication(exclude = {WebMvcAutoConfiguration.class}) -@EnableServiceComb public class SpringmvcServer { private static final Logger LOGGER = LoggerFactory.getLogger(SpringmvcServer.class); @@ -40,11 +37,13 @@ public static void main(final String[] args) throws Exception { } private static void assertPropertyCorrect() { - String result = DynamicPropertyFactory.getInstance() - .getStringProperty("test.unresolved.placeholder", null).get(); - if (!"jdbc:postgresql://${ip}:${port}/pt".equals(result)) { - LOGGER.error("tests for configuration error, stop"); - SCBEngine.getInstance().destroy(); + // spring environment will fail for unresolved placeholder property + try { + LegacyPropertyFactory.getStringProperty("test.unresolved.placeholder"); + } catch (IllegalArgumentException e) { + return; } + LOGGER.error("tests for configuration error, stop"); + SCBEngine.getInstance().destroy(); } } diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/UploadDownloadSchema.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/UploadDownloadSchema.java new file mode 100644 index 00000000000..ec80bedbec4 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/UploadDownloadSchema.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.servicecomb.springboot.springmvc.server; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.ws.rs.core.MediaType; + +@RestSchema(schemaId = "UploadDownloadSchema") +@RequestMapping(path = "/up/down") +public class UploadDownloadSchema { + @PostMapping(path = "/upload", consumes = MediaType.MULTIPART_FORM_DATA) + public String fileUpload(@RequestPart MultipartFile file, @RequestPart(value = "name") String name) throws Exception { + StringBuilder result = new StringBuilder(); + result.append(name).append(";"); + if (file == null || file.isEmpty()) { + return result.toString(); + } + try (InputStream is = file.getInputStream()) { + result.append(IOUtils.toString(is, StandardCharsets.UTF_8)); + } + return result.toString(); + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/resources/application.yml b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/resources/application.yml index 29c424bbbc1..7cd484f51bb 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/resources/application.yml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/resources/application.yml @@ -15,12 +15,6 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: springmvcboottest - -service_description: - name: springmvc - version: 0.0.3 - server: port: 8080 @@ -30,6 +24,13 @@ test: schema: placeholder/schema servicecomb: + service: + application: springmvcboottest + name: springmvc + version: 0.0.3 + registry: + sc: + address: http://127.0.0.1:30100 test: vert: transport: false diff --git a/demo/demo-spring-boot-transport/pom.xml b/demo/demo-spring-boot-transport/pom.xml index 7f33baec273..bd7650a3f26 100644 --- a/demo/demo-spring-boot-transport/pom.xml +++ b/demo/demo-spring-boot-transport/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-spring-boot-transport Java Chassis::Demo::Spring Boot::Transport @@ -35,6 +35,10 @@ + + org.apache.servicecomb + solution-basic + org.apache.servicecomb java-chassis-spring-boot-starter-servlet diff --git a/demo/demo-springmvc/pom.xml b/demo/demo-springmvc/pom.xml index 9ac0a6a9151..c307aa7741b 100644 --- a/demo/demo-springmvc/pom.xml +++ b/demo/demo-springmvc/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-springmvc Java Chassis::Demo::Spring MVC @@ -32,10 +32,18 @@ springmvc-client + + org.apache.servicecomb.demo + demo-schema + org.apache.servicecomb registry-service-center + + org.apache.servicecomb + registry-local + org.apache.servicecomb java-chassis-spring-boot-starter-standalone diff --git a/demo/demo-springmvc/springmvc-client/pom.xml b/demo/demo-springmvc/springmvc-client/pom.xml index 76d8ee06f4f..17b7bbcd709 100644 --- a/demo/demo-springmvc/springmvc-client/pom.xml +++ b/demo/demo-springmvc/springmvc-client/pom.xml @@ -23,41 +23,12 @@ org.apache.servicecomb.demo demo-springmvc - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT springmvc-client Java Chassis::Demo::Spring MVC::Client - - org.apache.servicecomb.demo - demo-schema - - - org.apache.servicecomb - provider-springmvc - - - org.apache.servicecomb - provider-pojo - - - org.apache.servicecomb - config-cc - - - org.apache.servicecomb - handler-fault-injection - - - - org.apache.servicecomb.demo - demo-signature - - - org.apache.servicecomb - foundation-test-scaffolding - @@ -71,24 +42,88 @@ springmvc-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java index 688b998c959..b827884d323 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java @@ -17,32 +17,30 @@ package org.apache.servicecomb.demo.springmvc; -import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.http.HttpStatus; +import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.DemoConst; import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.demo.controller.Controller; import org.apache.servicecomb.demo.controller.Person; import org.apache.servicecomb.demo.springmvc.client.CodeFirstRestTemplateSpringmvc; -import org.apache.servicecomb.demo.springmvc.client.ThirdSvc.ThirdSvcClient; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; import org.apache.servicecomb.provider.springmvc.reference.CseRestTemplate; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.provider.springmvc.reference.UrlWithProviderPrefixClientHttpRequestFactory; import org.apache.servicecomb.provider.springmvc.reference.UrlWithServiceNameClientHttpRequestFactory; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.annotation.ImportResource; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -50,12 +48,12 @@ import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; -import com.netflix.config.DynamicPropertyFactory; @SpringBootApplication -@EnableServiceComb +@ImportResource(value = "classpath*:META-INF/spring/*.bean.xml") public class SpringmvcClient { private static final Logger LOGGER = LoggerFactory.getLogger(SpringmvcClient.class); @@ -63,15 +61,22 @@ public class SpringmvcClient { private static RestTemplate templateUrlWithProviderPrefix = new CseRestTemplate(); - private static RestTemplate restTemplate; + private static RestOperations restTemplate; - private static Controller controller; + public static void main(String[] args) { + new SpringApplicationBuilder(SpringmvcClient.class).web(WebApplicationType.NONE).run(args); - public static void main(String[] args) throws Exception { - try { - new SpringApplicationBuilder(SpringmvcClient.class).web(WebApplicationType.NONE).run(args); + run(); + } + + private static void changeTransport(String microserviceName, String transport) { + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); + TestMgr.setMsg(microserviceName, transport); + } - run(); + public static void run() { + try { + runImpl(); } catch (Throwable e) { TestMgr.check("success", "failed"); LOGGER.error("-------------- test failed -------------"); @@ -79,31 +84,34 @@ public static void main(String[] args) throws Exception { LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); + LOGGER.info("-------------- last time updated checks(maybe more/less): 1344 -------------"); } - private static void changeTransport(String microserviceName, String transport) { - ArchaiusUtils.setProperty("servicecomb.references.transport." + microserviceName, transport); - TestMgr.setMsg(microserviceName, transport); - } - - public static void run() throws Exception { + private static void runImpl() throws Exception { testHttpClientsIsOk(); templateUrlWithServiceName.setRequestFactory(new UrlWithServiceNameClientHttpRequestFactory()); restTemplate = RestTemplateBuilder.create(); templateUrlWithProviderPrefix.setRequestFactory(new UrlWithProviderPrefixClientHttpRequestFactory("/pojo/rest")); - controller = BeanUtils.getBean("controller"); String prefix = "cse://springmvc"; String microserviceName = "springmvc"; try { - // this test class is intended for retry hanging issue JAV-127 + // only works in rest, highway does not have name param, because not defined. + templateUrlWithServiceName.getForObject(prefix + "/controller/sayhi?name=throwexception", String.class); + TestMgr.check("true", "false"); + } catch (Exception e) { + TestMgr.check("true", "true"); + } + try { + // only works in rest, highway does not have name param, because not defined. templateUrlWithServiceName.getForObject(prefix + "/controller/sayhi?name=throwexception", String.class); TestMgr.check("true", "false"); } catch (Exception e) { TestMgr.check("true", "true"); } + testHandler(microserviceName); CodeFirstRestTemplateSpringmvc codeFirstClient = BeanUtils.getContext().getBean(CodeFirstRestTemplateSpringmvc.class); @@ -122,13 +130,9 @@ private static void testHandler(String microserviceName) { } private static void testHttpClientsIsOk() { - TestMgr.check(HttpClients.getClient("registry") != null, true); - TestMgr.check(HttpClients.getClient("registry-watch") != null, true); TestMgr.check(HttpClients.getClient("http-transport-client") != null, true); TestMgr.check(HttpClients.getClient("http2-transport-client") != null, true); - TestMgr.check(HttpClients.getClient("registry", false) != null, true); - TestMgr.check(HttpClients.getClient("registry-watch", false) != null, true); TestMgr.check(HttpClients.getClient("http-transport-client", false) != null, true); TestMgr.check(HttpClients.getClient("http2-transport-client", false) != null, true); } @@ -157,7 +161,7 @@ private static void testRestTransport(String microserviceName, String prefix) { //0.5.0 later version metrics integration test @SuppressWarnings("unchecked") - Map metrics = restTemplate.getForObject(prefix + "/metrics", Map.class); + Map metrics = restTemplate.getForObject(prefix + "/scb/metrics", Map.class); // TestMgr.check(true, metrics.get("jvm(name=heapUsed,statistic=gauge)") != 0); TestMgr.check(true, metrics.size() > 0); @@ -167,7 +171,7 @@ private static void testRestTransport(String microserviceName, String prefix) { String content = restTemplate .getForObject("cse://springmvc/codeFirstSpringmvc/prometheusForTest", String.class); - String application = DynamicPropertyFactory.getInstance().getStringProperty("APPLICATION_ID", "").get(); + String application = LegacyPropertyFactory.getStringProperty("servicecomb.service.application", ""); TestMgr.check(true, content.contains( @@ -204,6 +208,7 @@ private static void testRestTransport(String microserviceName, String prefix) { TestMgr.check("true", "false"); } } catch (Exception e) { + LOGGER.error("", e); TestMgr.check("true", "false"); } } @@ -216,22 +221,11 @@ private static void testAllTransport(String microserviceName) { testControllerAllTransport(templateUrlWithServiceName, microserviceName); - testController(); - testRequiredBody(templateUrlWithServiceName, microserviceName); testSpringMvcDefaultValuesAllTransport(templateUrlWithServiceName, microserviceName); testSpringMvcDefaultValuesJavaPrimitiveAllTransport(templateUrlWithServiceName, microserviceName); - testThirdService(); } } - private static void testThirdService() { - ThirdSvcClient client = BeanUtils.getContext().getBean(ThirdSvcClient.class); - - Date date = new Date(); - ResponseEntity responseEntity = client.responseEntity(date); - TestMgr.check(date, responseEntity.getBody()); - } - private static void testControllerRest(RestTemplate template, String microserviceName) { String prefix = "cse://" + microserviceName; @@ -239,6 +233,14 @@ private static void testControllerRest(RestTemplate template, String microservic template.getForObject(prefix + "/controller/sayhi?name=world", String.class)); + TestMgr.check("hi world boot [world boot]", + template.getForObject(prefix + "/controller/sayhi?name=world boot", + String.class)); + + TestMgr.check("hi world boot [world boot]", + template.getForObject(prefix + "/controller/sayhi?name=world+boot", + String.class)); + TestMgr.check("hi world1 [world1]", template.getForObject(prefix + "/controller/sayhi?name={name}", String.class, @@ -270,8 +272,6 @@ private static void testControllerRest(RestTemplate template, String microservic } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), 503); } - - TestMgr.check("hi world [world]", controller.sayHi("world")); } private static void testControllerAllTransport(RestTemplate template, String microserviceName) { @@ -289,26 +289,6 @@ private static void testControllerAllTransport(RestTemplate template, String mic TestMgr.check(e.getStatusCode(), 400); } - TestMgr.check("hi world [world]", - template.getForObject(prefix + "/controller/sayhi?name=world", - String.class)); - - TestMgr.check("hi world1 [world1]", - template.getForObject(prefix + "/controller/sayhi?name={name}", - String.class, - "world1")); - TestMgr.check("hi hi 中国 [hi 中国]", - template.getForObject(prefix + "/controller/sayhi?name={name}", - String.class, - "hi 中国")); - - Map params = new HashMap<>(); - params.put("name", "world2"); - TestMgr.check("hi world2 [world2]", - template.getForObject(prefix + "/controller/sayhi?name={name}", - String.class, - params)); - TestMgr.check("hello world", template.postForObject(prefix + "/controller/sayhello/{name}", null, @@ -349,57 +329,6 @@ private static void testControllerAllTransport(RestTemplate template, String mic "ha")); } - private static void testController() { - TestMgr.check("hi world [world]", controller.sayHi("world")); - Person user = new Person(); - user.setName("world"); - TestMgr.check("ha world", controller.saySomething("ha", user)); - } - - private static void testRequiredBody(RestTemplate template, String microserviceName) { - String prefix = "cse://" + microserviceName; - Person user = new Person(); - - TestMgr.check("No user data found", - template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", - user, - String.class, - "ha")); - - user.setName("world"); - TestMgr.check("ha world", - template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", - user, - String.class, - "ha")); - - TestMgr.check("No user data found", - template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", - null, - String.class, - "ha")); - - TestMgr.check("No user name found", - template.postForObject(prefix + "/annotations/say", - "", - String.class, - "ha")); - TestMgr.check("test", - template.postForObject(prefix + "/annotations/say", - "test", - String.class, - "ha")); - - try { - template.postForObject(prefix + "/annotations/testRequiredBody", - null, - String.class); - TestMgr.fail("should fail"); - } catch (InvocationException e) { - TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); - } - } - private static void testSpringMvcDefaultValuesRest(RestTemplate template, String microserviceName) { String cseUrlPrefix = "cse://" + microserviceName + "/SpringMvcDefaultValues/"; String result = template.getForObject(cseUrlPrefix + "/query?d=10", String.class); diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java index ddf1a0d49d3..d30edc6b495 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java @@ -26,15 +26,15 @@ import java.util.HashMap; import java.util.Map; -import javax.servlet.http.Part; - import org.apache.commons.io.FileUtils; +import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.demo.CodeFirstRestTemplate; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.part.FilePart; import org.apache.servicecomb.provider.pojo.Invoker; import org.apache.servicecomb.provider.springmvc.reference.CseHttpEntity; -import org.apache.servicecomb.registry.RegistrationManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpEntity; @@ -45,7 +45,9 @@ import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; + +import jakarta.servlet.http.Part; @Component public class CodeFirstRestTemplateSpringmvc extends CodeFirstRestTemplate { @@ -72,8 +74,14 @@ interface UploadStreamAndResource { private TestContentType testContentType = new TestContentType(); + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + testResponse.setEnvironment(environment); + } + @Override - protected void testOnlyRest(String microservcieName, RestTemplate template, String cseUrlPrefix) { + protected void testOnlyRest(String microservcieName, RestOperations template, String cseUrlPrefix) { try { testUpload(template, cseUrlPrefix); } catch (IOException e) { @@ -92,7 +100,7 @@ protected void testOnlyRest(String microservcieName, RestTemplate template, Stri } @Override - protected void testOnlyHighway(RestTemplate template, String cseUrlPrefix) { + protected void testOnlyHighway(RestOperations template, String cseUrlPrefix) { testResponse.runHighway(); testObject.runHighway(); testGeneric.runHighway(); @@ -101,7 +109,7 @@ protected void testOnlyHighway(RestTemplate template, String cseUrlPrefix) { } @Override - protected void testAllTransport(String microserviceName, RestTemplate template, String cseUrlPrefix) { + protected void testAllTransport(String microserviceName, RestOperations template, String cseUrlPrefix) { testResponse.runAllTransport(); testObject.runAllTransport(); testGeneric.runAllTransport(); @@ -112,9 +120,7 @@ protected void testAllTransport(String microserviceName, RestTemplate template, super.testAllTransport(microserviceName, template, cseUrlPrefix); } - @SuppressWarnings("deprecation") -// TODO : upgrade to spring 5 will having warning's , we'll fix it later - private void testUpload(RestTemplate template, String cseUrlPrefix) throws IOException { + private void testUpload(RestOperations template, String cseUrlPrefix) throws IOException { String file1Content = "hello world"; File file1 = File.createTempFile("测 试", ".txt"); FileUtils.writeStringToFile(file1, file1Content, StandardCharsets.UTF_8, false); @@ -160,7 +166,7 @@ private void testUpload(RestTemplate template, String cseUrlPrefix) throws IOExc TestMgr.check(expect, result); } - private String testRestTemplateUpload(RestTemplate template, String cseUrlPrefix, File file1, File someFile) { + private String testRestTemplateUpload(RestOperations template, String cseUrlPrefix, File file1, File someFile) { MultiValueMap map = new LinkedMultiValueMap<>(); map.add("file1", new FileSystemResource(file1)); map.add("someFile", new FileSystemResource(someFile)); @@ -171,7 +177,7 @@ private String testRestTemplateUpload(RestTemplate template, String cseUrlPrefix String.class); } - private void testResponseEntity(String microserviceName, RestTemplate template, String cseUrlPrefix) { + private void testResponseEntity(String microserviceName, RestOperations template, String cseUrlPrefix) { Map body = new HashMap<>(); Date date = new Date(); body.put("date", date); @@ -179,7 +185,7 @@ private void testResponseEntity(String microserviceName, RestTemplate template, CseHttpEntity> httpEntity = new CseHttpEntity<>(body); httpEntity.addContext("contextKey", "contextValue"); - String srcName = RegistrationManager.INSTANCE.getMicroservice().getServiceName(); + String srcName = BootStrapProperties.readServiceName(environment); ResponseEntity responseEntity = template.exchange(cseUrlPrefix + "responseEntity", HttpMethod.POST, httpEntity, Date.class); @@ -201,48 +207,48 @@ private void testResponseEntity(String microserviceName, RestTemplate template, TestMgr.check(retryResult, 5); } - private void testCodeFirstTestForm(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstTestForm(RestOperations template, String cseUrlPrefix) { HttpHeaders formHeaders = new HttpHeaders(); formHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); Map map = new HashMap<>(); String code = "servicecomb%2bwelcome%40%23%24%25%5e%26*()%3d%3d"; map.put("form1", code); - HttpEntity> formEntiry = new HttpEntity<>(map, formHeaders); + HttpEntity> formEntry = new HttpEntity<>(map, formHeaders); TestMgr.check(code + "null", - template.postForEntity(cseUrlPrefix + "/testform", formEntiry, String.class).getBody()); + template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); map.put("form2", "hello"); TestMgr - .check(code + "hello", template.postForEntity(cseUrlPrefix + "/testform", formEntiry, String.class).getBody()); + .check(code + "hello", template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); } - private void testCodeFirstTestFormHighway(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstTestFormHighway(RestOperations template, String cseUrlPrefix) { HttpHeaders formHeaders = new HttpHeaders(); formHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); Map map = new HashMap<>(); String code = "servicecomb%2bwelcome%40%23%24%25%5e%26*()%3d%3d"; map.put("form1", code); map.put("form2", ""); - HttpEntity> formEntiry = new HttpEntity<>(map, formHeaders); - TestMgr.check(code + "", template.postForEntity(cseUrlPrefix + "/testform", formEntiry, String.class).getBody()); + HttpEntity> formEntry = new HttpEntity<>(map, formHeaders); + TestMgr.check(code + "", template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); map = new HashMap<>(); code = "servicecomb%2bwelcome%40%23%24%25%5e%26*()%3d%3d"; map.put("form1", code); map.put("form2", null); - formEntiry = new HttpEntity<>(map, formHeaders); + formEntry = new HttpEntity<>(map, formHeaders); TestMgr.check(code + "null", - template.postForEntity(cseUrlPrefix + "/testform", formEntiry, String.class).getBody()); + template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); } - private void testCodeFirstTestFormRest(RestTemplate template, String cseUrlPrefix) { + private void testCodeFirstTestFormRest(RestOperations template, String cseUrlPrefix) { HttpHeaders formHeaders = new HttpHeaders(); formHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); Map map = new HashMap<>(); String code = "servicecomb%2bwelcome%40%23%24%25%5e%26*()%3d%3d"; map.put("form1", code); map.put("form2", ""); - HttpEntity> formEntiry = new HttpEntity<>(map, formHeaders); + HttpEntity> formEntry = new HttpEntity<>(map, formHeaders); // Rest will have empty string, but users will try to avoid depend on this, This is different from highway - TestMgr.check(code + "", template.postForEntity(cseUrlPrefix + "/testform", formEntiry, String.class).getBody()); + TestMgr.check(code + "", template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); } } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ConsumerTestsAfterBootup.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ConsumerTestsAfterBootup.java deleted file mode 100644 index aabf0d0bd59..00000000000 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ConsumerTestsAfterBootup.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.apache.servicecomb.demo.springmvc.client; -/* - * 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. - */ - -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; - -import org.apache.servicecomb.core.BootListener; -import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.registry.RegistrationManager; -import org.springframework.stereotype.Component; - -/** - * Testing after bootup. - */ -@Component -public class ConsumerTestsAfterBootup implements BootListener { - private void testRegisterPath() { - TestMgr.check(RegistrationManager.INSTANCE.getMicroservice().getPaths().size(), 0); - } - - private void testSchemaContent() { - String content = RegistrationManager.INSTANCE.getMicroservice().getSchemaMap().get("SpringMVCSchema"); - TestMgr.check(content.replaceAll("\\s", ""), - readFile("SpringMVCSchema.yaml").replaceAll("[\\s#]", "")); - } - - private String readFile(String restController) { - // test code, make simple - try { - InputStream inputStream = this.getClass().getResource("/" + restController).openStream(); - byte[] buffer = new byte[2048 * 10]; - inputStream.skip(1000); - int len = inputStream.read(buffer); - TestMgr.check(2048 * 10 > len, true); - inputStream.close(); - return new String(buffer, 0, len, StandardCharsets.UTF_8); - } catch (IOException e) { - TestMgr.failed(e.getMessage(), e); - return null; - } - } - - @Override - public void onBootEvent(BootEvent event) { - if (event.getEventType() == BootListener.EventType.AFTER_REGISTRY) { - testRegisterPath(); - testSchemaContent(); - if (!TestMgr.isSuccess()) { - TestMgr.summary(); - throw new IllegalStateException("some tests are failed. "); - } - } - } -} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CustomEndpointDiscoveryFilter.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CustomEndpointDiscoveryFilter.java index aa3ee5d53cb..6bf78279d42 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CustomEndpointDiscoveryFilter.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CustomEndpointDiscoveryFilter.java @@ -17,10 +17,10 @@ package org.apache.servicecomb.demo.springmvc.client; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; import org.apache.servicecomb.registry.discovery.AbstractEndpointDiscoveryFilter; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; public class CustomEndpointDiscoveryFilter extends AbstractEndpointDiscoveryFilter { @Override @@ -31,7 +31,7 @@ protected String findTransportName(DiscoveryContext context, DiscoveryTreeNode p @Override protected Object createEndpoint(DiscoveryContext context, String transportName, String endpoint, - MicroserviceInstance instance) { + StatefulDiscoveryInstance instance) { return endpoint; } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ICompatible1xTestSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ICompatible1xTestSchema.java new file mode 100644 index 00000000000..ac8c10ecbda --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ICompatible1xTestSchema.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.springmvc.client; + +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + +import io.swagger.v3.oas.annotations.Operation; + +public interface ICompatible1xTestSchema { + String parameterName(int c, int d); + + @Operation(operationId = "parameterName", summary = "parameterName") + String parameterNamePartMatchLeft(int a, int d); + + @Operation(operationId = "parameterName", summary = "parameterName") + String parameterNamePartMatchRight(int c, int b); + + String parameterName(InvocationContext context, int c, int d); + + String parameterNameServerContext(int c, int d); + + String beanParameter(String notName, int notAge); +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/MicroserviceArray.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/MicroserviceArray.java deleted file mode 100644 index add5e6f1163..00000000000 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/MicroserviceArray.java +++ /dev/null @@ -1,34 +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.servicecomb.demo.springmvc.client; - -import java.util.List; - -import org.apache.servicecomb.registry.api.registry.Microservice; - -public class MicroserviceArray { - - private List services; - - public List getServices() { - return services; - } - - public void setServices(List services) { - this.services = services; - } -} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ResponseOKData.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ResponseOKData.java new file mode 100644 index 00000000000..df2bf9c5f42 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ResponseOKData.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.servicecomb.demo.springmvc.client; + +public class ResponseOKData { + private String errorCode; + + private String errorMessage; + + public ResponseOKData() { + + } + + public ResponseOKData(String errorCode, String errorMessage) { + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SchemeInterfaceSpringmvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SchemeInterfaceSpringmvc.java index 863f9b825b7..9bce39f0af7 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SchemeInterfaceSpringmvc.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SchemeInterfaceSpringmvc.java @@ -21,4 +21,8 @@ public interface SchemeInterfaceSpringmvc { int add(int a, int b); int reduce(int a, int b); + + String tailingSlash(int a, int b); + + String nonTailingSlash(int a, int b); } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ServiceCenterExample.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ServiceCenterExample.java deleted file mode 100644 index 558d02e0a8c..00000000000 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ServiceCenterExample.java +++ /dev/null @@ -1,55 +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.servicecomb.demo.springmvc.client; - -import java.net.URI; - -import org.springframework.http.HttpMethod; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; - -public class ServiceCenterExample { - - public static void main(String[] args) throws Exception { - RestTemplate template = new RestTemplate(); - template.getMessageConverters().add(new MappingJackson2HttpMessageConverter()); - MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("X-Tenant-Name", "default"); - - RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, - new URI("http://127.0.0.1:9980/registry/v3/microservices")); - ResponseEntity stringResponseEntity = template.exchange(requestEntity, String.class); - System.out.println(stringResponseEntity.getBody()); - ResponseEntity microseriveResponseEntity = template - .exchange(requestEntity, MicroserviceArray.class); - MicroserviceArray microserives = microseriveResponseEntity.getBody(); - System.out.println(microserives.getServices().get(1).getServiceId()); - - // instance - headers.add("X-ConsumerId", microserives.getServices().get(1).getServiceId()); - requestEntity = new RequestEntity<>(headers, HttpMethod.GET, - new URI("http://127.0.0.1:9980/registry/v3/microservices/" + microserives.getServices().get(1).getServiceId() - + "/instances")); - ResponseEntity microserviceInstanceResponseEntity = template.exchange(requestEntity, String.class); - System.out.println(microserviceInstanceResponseEntity.getBody()); - } -} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SpringMVCSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SpringMVCSchema.java index 6368c572725..42c912188af 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SpringMVCSchema.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SpringMVCSchema.java @@ -17,28 +17,30 @@ package org.apache.servicecomb.demo.springmvc.client; -import javax.servlet.http.HttpServletRequest; - import org.apache.servicecomb.provider.rest.common.RestSchema; 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 io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import io.swagger.annotations.Example; -import io.swagger.annotations.ExampleProperty; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletRequest; @RestSchema(schemaId = "SpringMVCSchema") @RequestMapping("/springMvcSchema") public class SpringMVCSchema { + // TODO: examples not serialized by yaml parser @ApiResponses({ - @ApiResponse(code = 200, response = String.class, message = "success", - examples = @Example({ - @ExampleProperty(value = "wget http://localhost/springMvcSchema/testApiExample", mediaType = "text"), - @ExampleProperty(value = "{name:hello}", mediaType = "application/json"), - @ExampleProperty(value = "{name:hello}", mediaType = "json")}) + @ApiResponse(responseCode = "200", content = @Content( + schema = @Schema(implementation = String.class, example = "wget http://localhost/springMvcSchema/testApiExample"), + examples = {@ExampleObject(value = "wget http://localhost/springMvcSchema/testApiExample", name = "text"), + @ExampleObject(value = "{name:hello}", name = "application/json"), + @ExampleObject(value = "{name:hello}", name = "json")}) + , description = "success" )}) @RequestMapping(path = "/testApiExample", method = RequestMethod.POST) public String testApiExample(@RequestBody String name, HttpServletRequest request) { @@ -46,11 +48,11 @@ public String testApiExample(@RequestBody String name, HttpServletRequest reques } @ApiResponses({ - @ApiResponse(code = 200, response = String.class, message = "success", - examples = @Example({ - @ExampleProperty(value = "wget http://localhost/springMvcSchema/testDefaultGetApiExample", mediaType = "text"), - @ExampleProperty(value = "{name:hello}", mediaType = "application/json"), - @ExampleProperty(value = "{name:hello}", mediaType = "json")}) + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class), + examples = {@ExampleObject(value = "wget http://localhost/springMvcSchema/testApiExample", name = "text"), + @ExampleObject(value = "{name:hello}", name = "application/json"), + @ExampleObject(value = "{name:hello}", name = "json")}) + , description = "success" )}) @RequestMapping(path = "/testDefaultGetApiExample") public String testDefaultGetApiExample(@RequestParam String name, HttpServletRequest request) { diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestAnnotationsSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestAnnotationsSchema.java new file mode 100644 index 00000000000..89f7862384b --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestAnnotationsSchema.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.springmvc.client; + +import org.apache.http.HttpStatus; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.controller.Person; +import org.apache.servicecomb.provider.springmvc.reference.CseRestTemplate; +import org.apache.servicecomb.provider.springmvc.reference.UrlWithServiceNameClientHttpRequestFactory; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +@Component +public class TestAnnotationsSchema implements CategorizedTestCase { + private static final String microserviceName = "springmvc"; + + private static final RestTemplate templateUrlWithServiceName = new CseRestTemplate(); + + @Override + public void testAllTransport() throws Exception { + templateUrlWithServiceName.setRequestFactory(new UrlWithServiceNameClientHttpRequestFactory()); + testRequiredBody(templateUrlWithServiceName, microserviceName); + testRegExpPath(); + } + + private void testRegExpPath() { + String prefix = "cse://" + microserviceName; + String result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a?name={name}", + String.class, "a"); + TestMgr.check("a", result); + result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a/b?name={name}", + String.class, "ab"); + TestMgr.check("ab", result); + result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a/b/c?name={name}", + String.class, "abc"); + TestMgr.check("abc", result); + } + + private static void testRequiredBody(RestTemplate template, String microserviceName) { + String prefix = "cse://" + microserviceName; + Person user = new Person(); + + TestMgr.check("No user data found", + template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", + user, + String.class, + "ha")); + + user.setName("world"); + TestMgr.check("ha world", + template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", + user, + String.class, + "ha")); + + TestMgr.check("No user data found", + template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", + null, + String.class, + "ha")); + + TestMgr.check("No user name found", + template.postForObject(prefix + "/annotations/say", + "", + String.class, + "ha")); + TestMgr.check("test", + template.postForObject(prefix + "/annotations/say", + "test", + String.class, + "ha")); + + try { + template.postForObject(prefix + "/annotations/testRequiredBody", + null, + String.class); + TestMgr.fail("should fail"); + } catch (InvocationException e) { + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestBigNumberSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestBigNumberSchema.java new file mode 100644 index 00000000000..3b70709e6d2 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestBigNumberSchema.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.servicecomb.demo.springmvc.client; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component +public class TestBigNumberSchema implements CategorizedTestCase { + interface IBigNumberSchema { + BigInteger bigInteger(BigInteger intHeader, BigInteger intQuery, BigInteger intForm); + + BigDecimal bigDecimal(BigDecimal decimalHeader, BigDecimal decimalQuery, BigDecimal decimalForm); + } + + @RpcReference(microserviceName = "springmvc", schemaId = "BigNumberSchema") + private IBigNumberSchema schema; + + @Override + public void testAllTransport() throws Exception { + testBigInteger(); + testBigDecimal(); + } + + public void testBigInteger() { + BigInteger result = schema.bigInteger(BigInteger.valueOf(100L), BigInteger.valueOf(3000000000000000000L), + BigInteger.valueOf(200L)); + TestMgr.check("3000000000000000300", result.toString()); + } + + public void testBigDecimal() { + BigDecimal a = BigDecimal.valueOf(100.1D); + BigDecimal b = BigDecimal.valueOf(300000000000000000.1D); + BigDecimal c = BigDecimal.valueOf(200.1D); + BigDecimal expected = a.add(b).add(c); + BigDecimal result = schema.bigDecimal(a, b, c); + TestMgr.check(expected, result); + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestContentType.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestContentType.java index 65fc1461dd7..aa0d9d0f63d 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestContentType.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestContentType.java @@ -17,24 +17,27 @@ package org.apache.servicecomb.demo.springmvc.client; -import javax.ws.rs.core.MediaType; - import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.provider.springmvc.reference.CseHttpEntity; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; + +import jakarta.ws.rs.core.MediaType; public class TestContentType { - private RestTemplate restTemplate = RestTemplateBuilder.create(); + private RestOperations restTemplate = RestTemplateBuilder.create(); public void runAllTest() { testGlobalSetting(); testApiOperation(); testRequestMapping(); + testProtoBuffer(); testResponseTypeOverwrite(); } @@ -53,15 +56,15 @@ private void testGlobalSetting() { private void testApiOperation() { HttpHeaders requestHeaders = new HttpHeaders(); - requestHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); + requestHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN); CseHttpEntity requestEntity = new CseHttpEntity<>("from testApiOperation", requestHeaders); ResponseEntity responseEntity = restTemplate .exchange("cse://springmvc/contentTypeSpringmvc/testApiOperation", HttpMethod.POST, requestEntity, String.class); TestMgr.check( - "testApiOperation: name=[from testApiOperation], request content-type=[" + MediaType.APPLICATION_JSON + "]", + "testApiOperation: name=[from testApiOperation], request content-type=[" + MediaType.TEXT_PLAIN + "]", responseEntity.getBody()); - TestMgr.check(MediaType.APPLICATION_JSON, extractContentType(responseEntity.getHeaders().getContentType())); + TestMgr.check(MediaType.TEXT_PLAIN, extractContentType(responseEntity.getHeaders().getContentType())); } private void testRequestMapping() { @@ -77,6 +80,24 @@ private void testRequestMapping() { TestMgr.check(MediaType.APPLICATION_JSON, extractContentType(responseEntity.getHeaders().getContentType())); } + + private void testProtoBuffer() { + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.add(HttpHeaders.CONTENT_TYPE, SwaggerConst.PROTOBUF_TYPE); + User user = new User(); + user.setIndex(100); + user.setName("hello"); + user.setNames(new String[] {"a1", "a2"}); + CseHttpEntity requestEntity = new CseHttpEntity<>(user, requestHeaders); + ResponseEntity responseEntity = restTemplate + .exchange("cse://springmvc/contentTypeSpringmvc/testProtoBuffer", HttpMethod.POST, + requestEntity, String.class); + TestMgr.check( + "testRequestMapping: name=[hello:100:[a1, a2]], request content-type=[application/protobuf]", + responseEntity.getBody()); + TestMgr.check(SwaggerConst.PROTOBUF_TYPE, extractContentType(responseEntity.getHeaders().getContentType())); + } + private void testResponseTypeOverwrite() { ResponseEntity responseEntity = restTemplate .getForEntity("cse://springmvc/contentTypeSpringmvcOverwrite/testResponseTypeOverwrite", String.class); diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestControllerImpl.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestControllerImpl.java index 37fd873f870..3264caa9fa9 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestControllerImpl.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestControllerImpl.java @@ -18,26 +18,67 @@ import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.controller.Controller; +import org.apache.servicecomb.demo.controller.Person; +import org.apache.servicecomb.demo.controller.PersonAlias; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.http.client.common.HttpUtils; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; - -import com.netflix.config.DynamicPropertyFactory; +import org.springframework.web.client.RestOperations; @Component public class TestControllerImpl implements CategorizedTestCase { - RestTemplate restTemplate = RestTemplateBuilder.create(); - private static final String SERVER = "servicecomb://springmvc"; + @Autowired + Controller controller; + + RestOperations restTemplate = RestTemplateBuilder.create(); + + @Override public void testRestTransport() throws Exception { testQueryParamSpecial(); + testResponseModel(); + } + + @Override + public void testAllTransport() throws Exception { + testControllerBodyModel(); + testControllerHeaderModel(); + } + + private void testControllerHeaderModel() throws Exception { + // HTTP header只允许 ASCII 字符。 + // 在Header参数中使用特殊字符,需要在发送请求前进行编码,在收到响应后进行解码,具体编码方式可以业务自行选择。 + // 如果不进行编码,则可能会出现乱码(HTTP协议历史上只标准化了ASCII字符,其他字符集可能工作,可能不工作)。 + String encodedResult = controller.sayHei(HttpUtils.encodeURLParam("中文HTTP Header")); + TestMgr.check("hei 中文HTTP Header", HttpUtils.decodeURLParam(encodedResult)); + + // 普通ASCII字符不需要编码 + TestMgr.check("hei ~!@#$%^&*()_+-={}[]|\\:\";''<>?,./AL340", + controller.sayHei("~!@#$%^&*()_+-={}[]|\\:\";''<>?,./AL340")); + } + + private void testControllerBodyModel() { + Person user = new Person(); + user.setName("world"); + TestMgr.check("ha world", controller.saySomething("ha", user)); + } + + private void testResponseModel() { + Person person = restTemplate.getForObject(SERVER + "/springmvc/controller/testResponseModel", Person.class); + TestMgr.check("jack", person.getName()); + + PersonAlias personAlias = restTemplate.getForObject(SERVER + "/springmvc/controller/testResponseModel", + PersonAlias.class); + TestMgr.check("jack", personAlias.getName()); } private void testQueryParamSpecial() { // vert.x and servlet container have different query parameter implementations - if (DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.test.vert.transport", true).get()) { + if (LegacyPropertyFactory.getBooleanProperty("servicecomb.test.vert.transport", true)) { TestMgr.check(restTemplate.getForObject( SERVER + "/springmvc/controller/sayHello1?name=you;me", String.class), "Hello you,v"); diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDataTypesAnnotationsSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDataTypesAnnotationsSchema.java new file mode 100644 index 00000000000..6234b0a99f6 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDataTypesAnnotationsSchema.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.springmvc.client; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component +public class TestDataTypesAnnotationsSchema implements CategorizedTestCase { + public interface DataTypesAnnotationsItf { + int[] testIntArrayQuery(int[] param); + + Integer[] testIntegerArrayQuery(Integer[] param); + } + + @RpcReference(schemaId = "DataTypesAnnotationsSchema", microserviceName = "springmvc") + private DataTypesAnnotationsItf client; + + @Override + public void testAllTransport() throws Exception { + testIntArrayQuery(); + testIntegerArrayQuery(); + } + + private void testIntArrayQuery() { + int[] request = new int[] {5, 11, 4}; + int[] result = client.testIntArrayQuery(request); + TestMgr.check(request.length, result.length); + TestMgr.check(request[1], result[1]); + } + + private void testIntegerArrayQuery() { + Integer[] request = new Integer[] {5, 11, 4}; + Integer[] result = client.testIntegerArrayQuery(request); + TestMgr.check(request.length, result.length); + TestMgr.check(request[1], result[1]); + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDateTimeSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDateTimeSchema.java index 234f49ff004..580192c71a1 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDateTimeSchema.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDateTimeSchema.java @@ -35,12 +35,14 @@ import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.CseHttpEntity; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTree; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; interface DateTimeSchemaInf { Date getDate(Date date); @@ -74,20 +76,22 @@ public class TestDateTimeSchema implements CategorizedTestCase { @RpcReference(microserviceName = "springmvc", schemaId = "DateTimeSchema") private DateTimeSchemaWithContextInf dateTimeSchemaWithContextInf; - private DiscoveryTree discoveryTree = new DiscoveryTree(); + private DiscoveryTree discoveryTree; - public TestDateTimeSchema() { - discoveryTree.addFilter(new CustomEndpointDiscoveryFilter()); - discoveryTree.sort(); - } + private SCBEngine scbEngine; - @Override - public void testRestTransport() throws Exception { + @Autowired + public void setScbEngine(SCBEngine scbEngine) { + this.scbEngine = scbEngine; + } + @Autowired + public void setDiscoveryManager(DiscoveryManager discoveryManager) { + this.discoveryTree = new DiscoveryTree(discoveryManager); + discoveryTree.setDiscoveryFilters(List.of(new CustomEndpointDiscoveryFilter())); } - @Override - public void testHighwayTransport() throws Exception { + public TestDateTimeSchema() { } @@ -126,10 +130,10 @@ private void testDateTimeSchema() { private void testDateTimeSchemaMulticast() throws Exception { DiscoveryContext context = new DiscoveryContext(); - VersionedCache serversVersionedCache = discoveryTree.discovery(context, "springmvctest", "springmvc", "0+"); - List enpoints = serversVersionedCache.data(); + VersionedCache serversVersionedCache = discoveryTree.discovery(context, "springmvctest", "springmvc"); + List endpoints = serversVersionedCache.data(); - for (String endpoint : enpoints) { + for (String endpoint : endpoints) { InvocationContext invocationContext = new InvocationContext(); invocationContext.addLocalContext(LoadBalanceFilter.SERVICECOMB_SERVER_ENDPOINT, endpoint); Date date = new Date(); @@ -144,18 +148,18 @@ private void testDateTimeSchemaMulticast() throws Exception { private Endpoint parseEndpoint(String endpointUri) throws Exception { URI formatUri = new URI(endpointUri); - Transport transport = SCBEngine.getInstance().getTransportManager().findTransport(formatUri.getScheme()); + Transport transport = scbEngine.getTransportManager().findTransport(formatUri.getScheme()); return new Endpoint(transport, endpointUri); } private void testDateTimeSchemaMulticastRestTemplate() throws Exception { DiscoveryContext context = new DiscoveryContext(); - VersionedCache serversVersionedCache = discoveryTree.discovery(context, "springmvctest", "springmvc", "0+"); - List enpoints = serversVersionedCache.data(); + VersionedCache serversVersionedCache = discoveryTree.discovery(context, "springmvctest", "springmvc"); + List endpoints = serversVersionedCache.data(); - RestTemplate restTemplate = RestTemplateBuilder.create(); + RestOperations restTemplate = RestTemplateBuilder.create(); - for (String endpoint : enpoints) { + for (String endpoint : endpoints) { CseHttpEntity entity = new CseHttpEntity<>(null); InvocationContext invocationContext = new InvocationContext(); invocationContext.addLocalContext(LoadBalanceFilter.SERVICECOMB_SERVER_ENDPOINT, endpoint); diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java index 37a24116a44..f1ba4b87095 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java @@ -17,24 +17,77 @@ package org.apache.servicecomb.demo.springmvc.client; +import java.net.URI; +import java.util.Collections; +import java.util.List; + import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.vertx.http.ReadStreamPart; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.apache.servicecomb.registry.DiscoveryManager; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class TestDownloadSchema implements CategorizedTestCase { + @Autowired + DiscoveryManager discoveryManager; + @Override public void testRestTransport() throws Exception { testDownloadFileAndDeleted(); + testDownloadFileAndDeletedCN(); testDownloadFileNotDeleted(); testDownloadFileWithNull(); + testSetContentTypeByResponseEntity(); + testResponseOKException(); + } + + private void testResponseOKException() { + List instances = + discoveryManager.findServiceInstances("springmvctest", "springmvc"); + String endpoint = instances.get(0).getEndpoints().stream() + .filter(item -> item.startsWith("rest")).findFirst().get(); + URI endpointItem = URI.create(endpoint); + RestTemplate template = new RestTemplate(); + + // This is for compatible usage. For best practise, any status code + // should have only one type of response. + ResponseEntity resultFail = template.getForEntity( + "http://" + endpointItem.getHost() + ":" + endpointItem.getPort() + + "/api/download/testResponseOKExceptionBean?exception=true", ResponseOKData.class); + TestMgr.check(200, resultFail.getStatusCode().value()); + TestMgr.check("code-005", resultFail.getBody().getErrorCode()); + TestMgr.check("error-005", resultFail.getBody().getErrorMessage()); + ResponseEntity resultOK = template.getForEntity( + "http://" + endpointItem.getHost() + ":" + endpointItem.getPort() + + "/api/download/testResponseOKExceptionBean?exception=false", boolean.class); + TestMgr.check(true, resultOK.getBody()); + + resultFail = template.getForEntity( + "http://" + endpointItem.getHost() + ":" + endpointItem.getPort() + + "/api/download/testResponseOKExceptionDownload?exception=true&content=ddd&contentType=plain/text", + ResponseOKData.class); + TestMgr.check(200, resultFail.getStatusCode().value()); + TestMgr.check("code-005", resultFail.getBody().getErrorCode()); + TestMgr.check("error-005", resultFail.getBody().getErrorMessage()); + + ResponseEntity resultPartOK = template.getForEntity( + "http://" + endpointItem.getHost() + ":" + endpointItem.getPort() + + "/api/download/testResponseOKExceptionDownload?exception=false&content=ddd&contentType=plain/text", + String.class); + TestMgr.check(200, resultPartOK.getStatusCode().value()); + TestMgr.check("ddd", resultPartOK.getBody()); } private void testDownloadFileAndDeleted() throws Exception { - RestTemplate restTemplate = RestTemplateBuilder.create(); + RestOperations restTemplate = RestTemplateBuilder.create(); ReadStreamPart readStreamPart = restTemplate .getForObject("servicecomb://springmvc/download/deleteAfterFinished?content=hello", ReadStreamPart.class); String hello = readStreamPart.saveAsString().get(); @@ -45,8 +98,21 @@ private void testDownloadFileAndDeleted() throws Exception { TestMgr.check(exists, false); } + private void testDownloadFileAndDeletedCN() throws Exception { + RestOperations restTemplate = RestTemplateBuilder.create(); + ReadStreamPart readStreamPart = restTemplate + .getForObject("servicecomb://springmvc/download/deleteAfterFinished?content={1}&fileName={2}", + ReadStreamPart.class, "hello", "中文"); + String hello = readStreamPart.saveAsString().get(); + TestMgr.check(hello, "hello"); + + boolean exists = restTemplate + .getForObject("servicecomb://springmvc/download/assertLastFileDeleted", boolean.class); + TestMgr.check(exists, false); + } + private void testDownloadFileWithNull() throws Exception { - RestTemplate restTemplate = RestTemplateBuilder.create(); + RestOperations restTemplate = RestTemplateBuilder.create(); ReadStreamPart readStreamPart = restTemplate .getForObject("servicecomb://springmvc/download/partIsNull?content=test", ReadStreamPart.class); String result = readStreamPart.saveAsString().get(); @@ -59,7 +125,7 @@ private void testDownloadFileWithNull() throws Exception { } private void testDownloadFileNotDeleted() throws Exception { - RestTemplate restTemplate = RestTemplateBuilder.create(); + RestOperations restTemplate = RestTemplateBuilder.create(); ReadStreamPart readStreamPart = restTemplate .getForObject("servicecomb://springmvc/download/notDeleteAfterFinished?content=hello", ReadStreamPart.class); String hello = readStreamPart.saveAsString().get(); @@ -69,4 +135,15 @@ private void testDownloadFileNotDeleted() throws Exception { .getForObject("servicecomb://springmvc/download/assertLastFileDeleted", boolean.class); TestMgr.check(exists, true); } + + private void testSetContentTypeByResponseEntity() throws Exception { + RestOperations restTemplate = RestTemplateBuilder.create(); + ResponseEntity responseEntity = restTemplate + .getForEntity( + "servicecomb://springmvc/download/setContentTypeByResponseEntity?content=hello&contentType=customType", + ReadStreamPart.class); + String hello = responseEntity.getBody().saveAsString().get(); + TestMgr.check(responseEntity.getHeaders().get(HttpHeaders.CONTENT_TYPE), Collections.singletonList("customType")); + TestMgr.check(hello, "hello"); + } } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestFactoryBean.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestFactoryBean.java new file mode 100644 index 00000000000..78295f47a8a --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestFactoryBean.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.servicecomb.demo.springmvc.client; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.springmvc.client.factory.ServiceBean; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +@Component +public class TestFactoryBean implements CategorizedTestCase, ApplicationContextAware { + ApplicationContext applicationContext; + + @Override + public void testRestTransport() throws Exception { + ServiceBean serviceBean = (ServiceBean) this.applicationContext.getBean("ServiceFactoryBean"); + TestMgr.check("a-3", serviceBean.getName()); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestGeneric.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestGeneric.java index 0a950b56ced..ce976f10d5b 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestGeneric.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestGeneric.java @@ -23,14 +23,14 @@ import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.provider.pojo.Invoker; -import org.apache.servicecomb.provider.springmvc.reference.CseRestTemplate; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.http.HttpStatus; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; public class TestGeneric { private CodeFirstSpringmvcIntf intf; - private RestTemplate restTemplate = new CseRestTemplate(); + private RestOperations restTemplate = RestTemplateBuilder.create(); private String prefix = "cse://springmvc/codeFirstSpringmvc"; diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestInvokeWhenServerNotReady.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestInvokeWhenServerNotReady.java index faa6f9874c5..899de3dcdca 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestInvokeWhenServerNotReady.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestInvokeWhenServerNotReady.java @@ -26,7 +26,7 @@ import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class TestInvokeWhenServerNotReady { @@ -44,7 +44,7 @@ private void startRestTemplateCall() { new Thread(() -> { for (int i = 0; i < 100; i++) { try { - RestTemplate template = RestTemplateBuilder.create(); + RestOperations template = RestTemplateBuilder.create(); template.getForObject("servicecomb://springmvc/upload/isServerStartUpSuccess", Boolean.class); } catch (Throwable e) { // ignore diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestManagementEndpoint.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestManagementEndpoint.java new file mode 100644 index 00000000000..edac80715a8 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestManagementEndpoint.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.springmvc.client; + +import java.util.Map; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.solution.basic.integration.ManagementEndpoint; +import org.springframework.stereotype.Component; + +@Component +public class TestManagementEndpoint implements CategorizedTestCase { + private static final String CONTENT_SCHEMA = """ + openapi: 3.0.1 + info: + title: swagger definition for org.apache.servicecomb.demo.springmvc.server.SchemeInterfaceSpringmvc + version: 1.0.0 + servers: + - url: /springmvc/schemaInterface + paths: + /add: + get: + operationId: add + parameters: + - name: a + in: query + required: true + schema: + minimum: 1 + type: integer + format: int32 + - name: b + in: query + required: true + schema: + minimum: 1 + type: integer + format: int32 + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: integer + format: int32 + /nonTailingSlash: + get: + operationId: nonTailingSlash + parameters: + - name: a + in: query + required: true + schema: + minimum: 1 + type: integer + format: int32 + - name: b + in: query + required: true + schema: + minimum: 1 + type: integer + format: int32 + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /tailingSlash/: + get: + operationId: tailingSlash + parameters: + - name: a + in: query + required: true + schema: + minimum: 1 + type: integer + format: int32 + - name: b + in: query + required: true + schema: + minimum: 1 + type: integer + format: int32 + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + components: {} + """; + + @RpcReference(microserviceName = "springmvc", schemaId = "SchemeInterfaceSpringmvc") + private SchemeInterfaceSpringmvc schemeInterfaceSpringmvc; + + @RpcReference(microserviceName = "springmvc", schemaId = ManagementEndpoint.NAME) + private ManagementEndpoint managementEndpoint; + + @Override + public void testAllTransport() throws Exception { + testSchemeInterfaceSpringmvcContentCorrect(); + } + + @Override + public void testRestTransport() throws Exception { + testSchemeInterfaceSpringmvcPathSlashCorrect(); + testSchemeInterfaceSpringmvcPathNonSlashCorrect(); + } + + private void testSchemeInterfaceSpringmvcPathSlashCorrect() { + String result = schemeInterfaceSpringmvc.tailingSlash(3, 5); + TestMgr.check("/api/springmvc/schemaInterface/tailingSlash/;" + + "/api/springmvc/schemaInterface/tailingSlash/;" + + "/api/springmvc/schemaInterface/tailingSlash/;" + + "-2", result); + } + + private void testSchemeInterfaceSpringmvcPathNonSlashCorrect() { + String result = schemeInterfaceSpringmvc.nonTailingSlash(3, 5); + TestMgr.check("/api/springmvc/schemaInterface/nonTailingSlash;" + + "/api/springmvc/schemaInterface/nonTailingSlash;" + + "/api/springmvc/schemaInterface/nonTailingSlash;" + + "-2", result); + } + + private void testSchemeInterfaceSpringmvcContentCorrect() { + Map contents = managementEndpoint.schemaContents(); + TestMgr.check(CONTENT_SCHEMA, contents.get("SchemeInterfaceSpringmvc")); + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestMaxHttpUrlLength.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestMaxHttpUrlLength.java index df71be8f442..08f9cdda1ee 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestMaxHttpUrlLength.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestMaxHttpUrlLength.java @@ -15,31 +15,32 @@ */ package org.apache.servicecomb.demo.springmvc.client; -import static javax.ws.rs.core.Response.Status.REQUEST_URI_TOO_LONG; +import static jakarta.ws.rs.core.Response.Status.REQUEST_URI_TOO_LONG; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; import com.google.common.base.Strings; -import com.netflix.config.DynamicPropertyFactory; @Component public class TestMaxHttpUrlLength implements CategorizedTestCase { @Override public void testRestTransport() throws Exception { - if (DynamicPropertyFactory.getInstance().getBooleanProperty("servicecomb.test.vert.transport", true).get()) { + if (LegacyPropertyFactory.getBooleanProperty("servicecomb.test.vert.transport", true)) { testUrlNotLongerThan4096(); } } private void testUrlNotLongerThan4096() { - RestTemplate restTemplate = RestTemplateBuilder.create(); + RestOperations restTemplate = RestTemplateBuilder.create(); // \r doesn't count for url length Since netty 4.1.88.Final See https://github.com/netty/netty/pull/12321 - String q = Strings.repeat("q", 4096 + 1 - "GET /api/springmvc/controller/sayhi?name=".length() - " HTTP/1.1\r".length()); + String q = Strings.repeat("q", + 4096 + 1 - "GET /api/springmvc/controller/sayhi?name=".length() - " HTTP/1.1\r".length()); TestMgr.check("hi " + q + " [" + q + "]", restTemplate.getForObject("cse://springmvc/springmvc/controller/sayhi?name=" + q, String.class)); @@ -53,14 +54,4 @@ private void testUrlNotLongerThan4096() { TestMgr.check(REQUEST_URI_TOO_LONG.getStatusCode(), e.getStatusCode()); } } - - @Override - public void testHighwayTransport() throws Exception { - - } - - @Override - public void testAllTransport() throws Exception { - - } } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestObject.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestObject.java index 9cd55cdcbbd..498a7d0c832 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestObject.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestObject.java @@ -26,13 +26,13 @@ import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.provider.pojo.Invoker; -import org.apache.servicecomb.provider.springmvc.reference.CseRestTemplate; -import org.springframework.web.client.RestTemplate; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.web.client.RestOperations; public class TestObject { private CodeFirstSpringmvcIntf intf; - private RestTemplate restTemplate = new CseRestTemplate(); + private RestOperations restTemplate = RestTemplateBuilder.create(); private String prefix = "cse://springmvc/codeFirstSpringmvc"; @@ -41,20 +41,17 @@ public TestObject() { } public void runRest() { - testEmptyObject_rest(); - testMapObject_rest(); - testObject(); - testListObject(); - testHolderObject(); + } public void runHighway() { - testEmptyObject_highway(); - testMapObject_highway(); + } public void runAllTransport() { testObject(); + testMapObject(); + testEmptyObject(); testListObject(); testHolderObject(); } @@ -82,19 +79,7 @@ private void testListObject() { } @SuppressWarnings("unchecked") - private void testMapObject_rest() { - Map map = Collections.singletonMap("k", "v"); - Map result = intf.testMapObject(map); - TestMgr.check("{k=v}", result); - TestMgr.check(LinkedHashMap.class, result.getClass()); - - result = restTemplate.postForObject(prefix + "/mapObject", map, Map.class); - TestMgr.check("{k=v}", result); - TestMgr.check(LinkedHashMap.class, result.getClass()); - } - - @SuppressWarnings("unchecked") - private void testMapObject_highway() { + private void testMapObject() { Map map = Collections.singletonMap("k", "v"); Map result = intf.testMapObject(map); TestMgr.check("{k=v}", result); @@ -106,16 +91,7 @@ private void testMapObject_highway() { TestMgr.check(LinkedHashMap.class, result.getClass()); } - private void testEmptyObject_highway() { - // This is a behavior change in 2.0.0, before 2.0.0 this return null - EmptyObject result = intf.testEmpty(new EmptyObject()); - TestMgr.check(result instanceof EmptyObject, true); - - result = restTemplate.postForObject(prefix + "/emptyObject", new EmptyObject(), EmptyObject.class); - TestMgr.check(result instanceof EmptyObject, true); - } - - private void testEmptyObject_rest() { + private void testEmptyObject() { EmptyObject result = intf.testEmpty(new EmptyObject()); TestMgr.check(EmptyObject.class, result.getClass()); @@ -175,6 +151,8 @@ private void testObject() { TestMgr.check(true, List.class.isAssignableFrom(result.getClass())); // generic + // This test case for HIGHWAY is not stable. + // When proto schema contains Holder message, the result type is Map. Or the result is Holder(using json). Holder holder = new Holder<>("v"); result = intf.testObject(holder); TestMgr.check("v", ((Map) result).get("value")); diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestResponse.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestResponse.java index bbafaa0d41b..bede84dadaf 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestResponse.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestResponse.java @@ -20,14 +20,15 @@ import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.compute.GenericParam; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.provider.pojo.Invoker; -import org.apache.servicecomb.registry.RegistrationManager; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.core.env.Environment; import org.springframework.http.ResponseEntity; import com.fasterxml.jackson.databind.exc.InvalidFormatException; @@ -35,6 +36,12 @@ public class TestResponse { private CodeFirstSpringmvcIntf intf; + private Environment environment; + + public void setEnvironment(Environment environment) { + this.environment = environment; + } + public TestResponse() { intf = Invoker.createProxy("springmvc", "codeFirst", CodeFirstSpringmvcIntf.class); } @@ -63,7 +70,7 @@ public void runAllTransport() { } private void testCseResponse() { - String srcName = RegistrationManager.INSTANCE.getMicroservice().getServiceName(); + String srcName = BootStrapProperties.readServiceName(environment); Response cseResponse = intf.cseResponse(); TestMgr.check("User [name=nameA, age=100, index=0]", cseResponse.getResult()); TestMgr.check("h1v " + srcName, cseResponse.getHeader("h1")); @@ -72,7 +79,7 @@ private void testCseResponse() { } private void testCseResponseCorrect() { - String srcName = RegistrationManager.INSTANCE.getMicroservice().getServiceName(); + String srcName = BootStrapProperties.readServiceName(environment); Response cseResponse = intf.cseResponseCorrect(); TestMgr.check("User [name=nameA, age=100, index=0]", cseResponse.getResult()); TestMgr.check("h1v " + srcName, cseResponse.getHeader("h1")); @@ -83,14 +90,14 @@ private void testCseResponseCorrect() { private void testResponseEntity() { Date date = new Date(); - String srcName = RegistrationManager.INSTANCE.getMicroservice().getServiceName(); + String srcName = BootStrapProperties.readServiceName(environment); ResponseEntity responseEntity = intf.responseEntity(date); TestMgr.check(date, responseEntity.getBody()); TestMgr.check("h1v " + srcName, responseEntity.getHeaders().getFirst("h1")); TestMgr.check("h2v " + srcName, responseEntity.getHeaders().getFirst("h2")); - TestMgr.check(202, responseEntity.getStatusCodeValue()); + TestMgr.check(202, responseEntity.getStatusCode().value()); } private void testvoidResponse() { diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestRestTemplate.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestRestTemplate.java index 74c7f7ff522..a9d02553bf7 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestRestTemplate.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestRestTemplate.java @@ -25,10 +25,10 @@ import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; public class TestRestTemplate { - private RestTemplate restTemplate = RestTemplateBuilder.create(); + private RestOperations restTemplate = RestTemplateBuilder.create(); public void runAllTest() { testvoidResponse(); @@ -46,19 +46,19 @@ public void runRest() { private void testvoidResponse() { ResponseEntity resultEntity = restTemplate .getForEntity("cse://springmvc/codeFirstSpringmvc/testvoidInRestTemplate", void.class); - Assert.isTrue(200 == resultEntity.getStatusCodeValue(), "void return type invocation failed"); + Assert.isTrue(200 == resultEntity.getStatusCode().value(), "void return type invocation failed"); } private void testVoidResponse() { ResponseEntity resultEntity = restTemplate .getForEntity("cse://springmvc/codeFirstSpringmvc/testVoidInRestTemplate", Void.class); - Assert.isTrue(200 == resultEntity.getStatusCodeValue(), "Void return type invocation failed"); + Assert.isTrue(200 == resultEntity.getStatusCode().value(), "Void return type invocation failed"); resultEntity = restTemplate .getForEntity("servicecomb://springmvc/codeFirstSpringmvc/testVoidInRestTemplate", Void.class); - Assert.isTrue(200 == resultEntity.getStatusCodeValue(), "Void return type invocation failed"); + Assert.isTrue(200 == resultEntity.getStatusCode().value(), "Void return type invocation failed"); resultEntity = restTemplate .getForEntity("servicecomb://springmvc/codeFirstSpringmvc/testDefaultGetApiExample", Void.class); - Assert.isTrue(200 == resultEntity.getStatusCodeValue(), "Void return type invocation failed"); + Assert.isTrue(200 == resultEntity.getStatusCode().value(), "Void return type invocation failed"); } private void checkAllVoidTestResult() { diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestRetrySchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestRetrySchema.java index f6a597a8573..501ff30f1fc 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestRetrySchema.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestRetrySchema.java @@ -23,7 +23,7 @@ import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class TestRetrySchema implements CategorizedTestCase { @@ -36,7 +36,7 @@ interface RetrySchemaInf { @RpcReference(microserviceName = "springmvc", schemaId = "RetrySchema") private RetrySchemaInf retrySchemaInf; - RestTemplate restTemplate = RestTemplateBuilder.create(); + RestOperations restTemplate = RestTemplateBuilder.create(); private static final String SERVER = "servicecomb://springmvc"; diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestThirdPartyRegistration.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestThirdPartyRegistration.java index 6c375ca29a0..8dc088010fb 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestThirdPartyRegistration.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestThirdPartyRegistration.java @@ -17,37 +17,22 @@ package org.apache.servicecomb.demo.springmvc.client; -import java.util.ArrayList; import java.util.Date; -import java.util.List; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.provider.pojo.Invoker; -import org.apache.servicecomb.registry.RegistrationManager; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; -import com.netflix.config.DynamicPropertyFactory; - @Component public class TestThirdPartyRegistration implements BootListener, CategorizedTestCase { + @Autowired private ThirdPartyService thirdPartyService; @Override public void onAfterRegistry(BootEvent event) { - List endpoints = new ArrayList<>(); - if (DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.test.vert.transport", true).get()) { - endpoints.add("rest://localhost:8080?sslEnabled=false&urlPrefix=%2Fapi"); - } else { - endpoints.add("rest://localhost:8080?sslEnabled=false"); - } - RegistrationManager.INSTANCE.registerMicroserviceMappingByEndpoints("testServiceName", - "1.0.1", endpoints, ThirdPartyService.class); - thirdPartyService = Invoker.createProxy("testServiceName", - "testServiceName", ThirdPartyService.class); } @Override @@ -55,11 +40,11 @@ public void testRestTransport() throws Exception { Date date = new Date(); ResponseEntity responseEntity = thirdPartyService.responseEntity(date); TestMgr.check(date, responseEntity.getBody()); - // Third party invocation do not pass cse-context to the target + // Third party invocation will pass cse-context to the target too TestMgr.check("h1v null", responseEntity.getHeaders().getFirst("h1")); TestMgr.check("h2v null", responseEntity.getHeaders().getFirst("h2")); - TestMgr.check(202, responseEntity.getStatusCodeValue()); + TestMgr.check(202, responseEntity.getStatusCode().value()); } @Override diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestThirdSvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestThirdSvc.java new file mode 100644 index 00000000000..98592c9bce2 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestThirdSvc.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.client; + +import java.util.Date; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.springmvc.client.ThirdSvc.ThirdSvcClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; + +@Component +public class TestThirdSvc implements CategorizedTestCase { + private ThirdSvcClient client; + + @Autowired + public void setThirdSvcClient(ThirdSvcClient client) { + this.client = client; + } + + @Override + public void testAllTransport() throws Exception { + testResponseEntity(); + + testDifferentParameterName(); + } + + private void testDifferentParameterName() { + String result = client.getAuthorization("test", "param", "auth"); + TestMgr.check("testparamauth", result); + } + + private void testResponseEntity() { + Date date = new Date(); + ResponseEntity responseEntity = client.responseEntity(date); + TestMgr.check(date, responseEntity.getBody()); + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestTransportSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestTransportSchema.java new file mode 100644 index 00000000000..d818ed439d4 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestTransportSchema.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.client; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component +public class TestTransportSchema implements CategorizedTestCase { + interface TransportClient { + boolean restTransport(); + + boolean highwayTransport(); + } + + @RpcReference(microserviceName = "springmvc", schemaId = "TransportSchema") + private TransportClient transportClient; + + @Override + public void testRestTransport() throws Exception { + testTransportSchema(); + } + + @Override + public void testHighwayTransport() throws Exception { + testTransportSchema(); + } + + @Override + public void testAllTransport() throws Exception { + testTransportSchema(); + } + + private void testTransportSchema() { + TestMgr.check(true, transportClient.highwayTransport()); + TestMgr.check(true, transportClient.restTransport()); + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestUploadSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestUploadSchema.java index 8acc9bdb02e..0e6d810d4a5 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestUploadSchema.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestUploadSchema.java @@ -41,7 +41,7 @@ import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class TestUploadSchema implements CategorizedTestCase { @@ -59,11 +59,13 @@ public void testRestTransport() throws Exception { testUploadMultiBigFiles(); testFileUploadMultiRpc(); testUploadFileAndAttribute(); + testUploadFileAndAttributeCN(); testUploadFileRequestPartAttribute(); + testUploadFileRequestPartAttributeList(); } private void testServerStartupSuccess() { - RestTemplate template = RestTemplateBuilder.create(); + RestOperations template = RestTemplateBuilder.create(); boolean result = template.getForObject("servicecomb://springmvc/upload/isServerStartUpSuccess", Boolean.class); TestMgr.check(result, true); } @@ -83,7 +85,7 @@ private void testUploadMultiBigFiles() throws Exception { fo.close(); } - RestTemplate template = RestTemplateBuilder.create(); + RestOperations template = RestTemplateBuilder.create(); MultiValueMap map = new LinkedMultiValueMap<>(); for (int i = 0; i < fileNum; i++) { @@ -111,7 +113,7 @@ private void testFileUploadMultiRpc() throws IOException { } private void testUploadFileAndAttribute() throws Exception { - RestTemplate template = RestTemplateBuilder.create(); + RestOperations template = RestTemplateBuilder.create(); Map map = new HashMap<>(); String message = "hi"; File file = File.createTempFile("file", ".txt"); @@ -126,8 +128,25 @@ private void testUploadFileAndAttribute() throws Exception { TestMgr.check("hi test", result); } + private void testUploadFileAndAttributeCN() throws Exception { + RestOperations template = RestTemplateBuilder.create(); + Map map = new HashMap<>(); + String message = "hi"; + File file = File.createTempFile("中文名称", ".txt"); + FileUtils.writeStringToFile(file, "test", StandardCharsets.UTF_8, false); + + map.put("file", new FileSystemResource(file)); + map.put("attribute", message); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA); + String result = template.postForObject("servicecomb://springmvc/upload/uploadFileAndAttribute", + new HttpEntity<>(map, headers), String.class); + TestMgr.check("hi test", result); + } + + private void testUploadFileRequestPartAttribute() throws Exception { - RestTemplate template = RestTemplateBuilder.create(); + RestOperations template = RestTemplateBuilder.create(); Map map = new HashMap<>(); String message = "hi"; File file = File.createTempFile("file", ".txt"); @@ -141,4 +160,25 @@ private void testUploadFileRequestPartAttribute() throws Exception { new HttpEntity<>(map, headers), String.class); TestMgr.check("hi test", result); } + + private void testUploadFileRequestPartAttributeList() throws Exception { + RestOperations template = RestTemplateBuilder.create(); + MultiValueMap map = new LinkedMultiValueMap<>(); + String message1 = "msg1"; + String message2 = "msg2"; + File file1 = File.createTempFile("file1", ".txt"); + FileUtils.writeStringToFile(file1, "test1", StandardCharsets.UTF_8, false); + File file2 = File.createTempFile("file2", ".txt"); + FileUtils.writeStringToFile(file2, "test2", StandardCharsets.UTF_8, false); + + map.add("files", new FileSystemResource(file1)); + map.add("files", new FileSystemResource(file2)); + map.add("attributes", message1); + map.add("attributes", message2); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA); + String result = template.postForObject("servicecomb://springmvc/upload/uploadFileRequestPartAttributeList", + new HttpEntity<>(map, headers), String.class); + TestMgr.check("test1test2msg1msg2", result); + } } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestWeakSpringmvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestWeakSpringmvc.java index 3c465cf3c88..df9f8236215 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestWeakSpringmvc.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestWeakSpringmvc.java @@ -32,7 +32,7 @@ import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; import com.fasterxml.jackson.core.JsonProcessingException; @@ -97,7 +97,7 @@ interface SpecialNameModelInf { @Component public class TestWeakSpringmvc implements CategorizedTestCase { - @Value("${APPLICATION_ID}") + @Value("${servicecomb.service.application}") private String applicationName; @RpcReference(microserviceName = "springmvc", schemaId = "weakSpringmvc") @@ -115,20 +115,10 @@ public class TestWeakSpringmvc implements CategorizedTestCase { @RpcReference(microserviceName = "springmvc", schemaId = "weakSpringmvc") private SpecialNameModelInf specialNameModelInf; - @RpcReference(microserviceName = "${APPLICATION_ID}:springmvc", schemaId = "weakSpringmvc") + @RpcReference(microserviceName = "${servicecomb.service.application}:springmvc", schemaId = "weakSpringmvc") private SpecialNameModelInf specialNameModelInfWithAppId; - private RestTemplate restTemplate = RestTemplateBuilder.create(); - - @Override - public void testRestTransport() throws Exception { - - } - - @Override - public void testHighwayTransport() throws Exception { - - } + private RestOperations restTemplate = RestTemplateBuilder.create(); @Override public void testAllTransport() throws Exception { @@ -138,10 +128,10 @@ public void testAllTransport() throws Exception { testGenericsModel(); - testSpecailNameModel(); + testSpecialNameModel(); } - private void testSpecailNameModel() { + private void testSpecialNameModel() { SpecialNameModel model = new SpecialNameModel(); model.setaIntName(30); SpecialNameModel result = specialNameModelInf.specialNameModel(0, model); @@ -180,7 +170,6 @@ private void testGenerics() { TestMgr.check("hello", nameListResult.get(0).get(0)); } - @SuppressWarnings({"deprecation"}) private void testDiffName() { TestMgr.check(7, diffNames.differentName(2, 3)); TestMgr.check(8, diffNames2.differentName(2, 3)); @@ -192,8 +181,8 @@ private void testDiffName() { args.put("x", 2); args.put("y", 3); TestMgr.check(7, InvokerUtils.syncInvoke("springmvc", - "weakSpringmvc", "differentName", args)); + "weakSpringmvc", "differentName", args, Integer.class)); TestMgr.check(7, InvokerUtils.syncInvoke(applicationName + ":springmvc", - "weakSpringmvc", "differentName", args)); + "weakSpringmvc", "differentName", args, Integer.class)); } } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdPartyService.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdPartyService.java index e8a1c93d144..e31e1183333 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdPartyService.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdPartyService.java @@ -19,19 +19,23 @@ import java.util.Date; -import org.apache.servicecomb.swagger.extend.annotations.ResponseHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import io.swagger.annotations.ResponseHeader; +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; @RequestMapping(path = "/codeFirstSpringmvc", produces = MediaType.APPLICATION_JSON_VALUE) public interface ThirdPartyService { - @ResponseHeaders({@ResponseHeader(name = "h1", response = String.class), - @ResponseHeader(name = "h2", response = String.class)}) + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Date.class)) + , description = "", + headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), + @Header(name = "h2", schema = @Schema(implementation = String.class))}) @RequestMapping(path = "/responseEntity", method = RequestMethod.POST) ResponseEntity responseEntity(@RequestAttribute("date") Date date); } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdSvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdSvc.java index ae855e2d537..3cbd8dd97dd 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdSvc.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdSvc.java @@ -16,31 +16,91 @@ */ package org.apache.servicecomb.demo.springmvc.client; +import java.util.ArrayList; import java.util.Date; +import java.util.List; -import org.apache.servicecomb.provider.pojo.registry.ThirdServiceWithInvokerRegister; -import org.apache.servicecomb.swagger.extend.annotations.ResponseHeaders; +import org.apache.servicecomb.localregistry.RegistryBean; +import org.apache.servicecomb.localregistry.RegistryBean.Instance; +import org.apache.servicecomb.localregistry.RegistryBean.Instances; +import org.apache.servicecomb.provider.pojo.Invoker; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestAttribute; +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 io.swagger.annotations.ResponseHeader; +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; @Configuration -public class ThirdSvc extends ThirdServiceWithInvokerRegister { +public class ThirdSvc { + @Autowired + Environment environment; + @RequestMapping(path = "/codeFirstSpringmvc") public interface ThirdSvcClient { - @ResponseHeaders({@ResponseHeader(name = "h1", response = String.class), - @ResponseHeader(name = "h2", response = String.class)}) + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Date.class)) + , description = "", + headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), + @Header(name = "h2", schema = @Schema(implementation = String.class))}) @RequestMapping(path = "/responseEntity", method = RequestMethod.POST) ResponseEntity responseEntity(@RequestAttribute("date") Date date); + + @GetMapping(value = "/getAuthorization", produces = {"application/json"}) + String getAuthorization( + @RequestHeader(value = "test") String test, + @RequestParam(value = "param") String param, + @RequestHeader("Authorization") String authorization); + } + + @Bean + public RegistryBean thirdRegistryBean() { + String endpoint; + if (environment.getProperty("servicecomb.test.vert.transport", boolean.class, true)) { + endpoint = "rest://localhost:8080?sslEnabled=false&urlPrefix=%2Fapi"; + } else { + endpoint = "rest://localhost:8080?sslEnabled=false"; + } + + return new RegistryBean().addSchemaInterface("schema-1", ThirdSvcClient.class) + .setAppId("springmvctest") + .setServiceName("3rd-svc") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of(new Instance().setEndpoints(List.of(endpoint))))); } - public ThirdSvc() { - super("3rd-svc"); + @Bean + public ThirdSvcClient thirdSvcClient() { + return Invoker.createProxy("3rd-svc", "schema-1", ThirdSvcClient.class); + } + + @Bean + public RegistryBean thirdPartyServiceRegistryBean() { + List endpoints = new ArrayList<>(); + if (environment.getProperty("servicecomb.test.vert.transport", boolean.class, true)) { + endpoints.add("rest://localhost:8080?sslEnabled=false&urlPrefix=%2Fapi"); + } else { + endpoints.add("rest://localhost:8080?sslEnabled=false"); + } + + return new RegistryBean().addSchemaInterface("testServiceName", ThirdPartyService.class) + .setAppId("springmvctest") + .setServiceName("testServiceName") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of(new Instance().setEndpoints(endpoints)))); + } - addSchema("schema-1", ThirdSvcClient.class); + @Bean + public ThirdPartyService thirdPartyService() { + return Invoker.createProxy("testServiceName", "testServiceName", ThirdPartyService.class); } } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceBean.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceBean.java new file mode 100644 index 00000000000..1a25838d083 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceBean.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.servicecomb.demo.springmvc.client.factory; + +public class ServiceBean { + private final String name; + + public ServiceBean(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceFactoryBean.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceFactoryBean.java new file mode 100644 index 00000000000..9bee6805c48 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceFactoryBean.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.servicecomb.demo.springmvc.client.factory; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component("ServiceFactoryBean") +public class ServiceFactoryBean implements FactoryBean { + + ServiceWithReference serviceWithReference; + + @Autowired + public ServiceFactoryBean(ServiceWithReference serviceWithReference) { + this.serviceWithReference = serviceWithReference; + } + + @Override + public ServiceBean getObject() throws Exception { + return new ServiceBean(serviceWithReference.test("a")); + } + + @Override + public Class getObjectType() { + return ServiceBean.class; + } + + @Override + public boolean isSingleton() { + return true; + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReference.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReference.java new file mode 100644 index 00000000000..f371bf35148 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReference.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.client.factory; + +public interface ServiceWithReference { + String test(String name); +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReferenceImpl.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReferenceImpl.java new file mode 100644 index 00000000000..a5f8d7c6bb1 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReferenceImpl.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.client.factory; + +import org.apache.servicecomb.demo.springmvc.client.SchemeInterfaceSpringmvc; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component +public class ServiceWithReferenceImpl implements ServiceWithReference { + @RpcReference(schemaId = "SchemeInterfaceSpringmvc", microserviceName = "springmvc") + private SchemeInterfaceSpringmvc springmvc; + + @Override + public String test(String name) { + return name + "-" + springmvc.add(1, 2); + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/resources/SpringMVCSchema.yaml b/demo/demo-springmvc/springmvc-client/src/main/resources/SpringMVCSchema.yaml index 34031a987ec..db67a5779a9 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/resources/SpringMVCSchema.yaml +++ b/demo/demo-springmvc/springmvc-client/src/main/resources/SpringMVCSchema.yaml @@ -19,53 +19,53 @@ ################################################################################ ################################################################################ ################################################################################ - ---- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for org.apache.servicecomb.demo.springmvc.client.SpringMVCSchema" - x-java-interface: "gen.swagger.SpringMVCSchemaIntf" -basePath: "/springMvcSchema" -schemes: - - "http" -consumes: - - "application/json" -produces: - - "application/json" + title: swagger definition for org.apache.servicecomb.demo.springmvc.client.SpringMVCSchema + version: 1.0.0 +servers: +- url: /springMvcSchema paths: /testApiExample: post: - operationId: "testApiExample" - parameters: - - in: "body" - name: "name" - required: true - schema: - type: "string" + operationId: testApiExample + requestBody: + content: + application/json: + schema: + type: string + required: true + x-name: name responses: "200": - description: "success" - examples: - text: "wget http://localhost/springMvcSchema/testApiExample" - application/json: "{name:hello}" - json: "{name:hello}" - schema: - type: "string" + description: success + headers: {} + content: + application/json: + schema: + type: string + description: "" + nullable: false + example: wget http://localhost/springMvcSchema/testApiExample /testDefaultGetApiExample: get: - operationId: "testDefaultGetApiExample" + operationId: testDefaultGetApiExample parameters: - - name: "name" - in: "query" - required: true - type: "string" + - name: name + in: query + required: true + schema: + type: string responses: "200": - description: "success" - examples: - text: "wget http://localhost/springMvcSchema/testDefaultGetApiExample" - application/json: "{name:hello}" - json: "{name:hello}" - schema: - type: "string" + description: success + headers: {} + content: + application/json: + schema: + type: string + description: "" + nullable: false + example: "" +components: + schemas: {} diff --git a/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml b/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml index bd46ceff97c..2fe0f881f55 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml +++ b/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml @@ -15,25 +15,18 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: springmvctest -service_description: - name: springmvcClient - version: 0.0.1 servicecomb: service: - registry: + application: springmvctest + name: springmvcClient + version: 0.0.1 + registry: + sc: address: http://127.0.0.1:30100 autodiscovery: true - client: - timeout: - idle: 60 - watch: 60 - instance: - healthCheck: - interval: 3 - pull: - interval: 90 - watch: true + healthCheckIntervalInSeconds: 3 + pollIntervalInMillis: 90000 + watch: true rest: client: @@ -41,7 +34,10 @@ servicecomb: compression: true references: version-rule: 0+ - + request: + clientRequestHeaderFilterEnabled: + # Do not copy cse-context to target for microservice(testServiceName) + testServiceName: true invocation: timeout: check: @@ -58,9 +54,6 @@ servicecomb: userDefinedEndpoint.enabled: true strategy: name: WeightedResponse - retryEnabled: true - retryOnSame: 1 - retryOnNext: 1 filter.status.enabled: false fallbackpolicy: Consumer: @@ -111,36 +104,23 @@ servicecomb: httpStatus: 421 percent: 50 -# test governance retry + # test governance retry matchGroup: retry-governance: | matches: - apiPath: prefix: "/retry/governance/" + retry-success: | + matches: + - apiPath: + prefix: "/codeFirstSpringmvc/retrySuccess" retry: retry-governance: | maxAttempts: 2 retryOnResponseStatus: [500] - -cse: - fallback: - Consumer: - springmvc: - codeFirstBizkeeperTest: - testTimeout: - enabled: false - isolation: - Consumer: - springmvc: - codeFirstBizkeeperTest: - testTimeout: - timeout: - enabled: true - timeoutInMilliseconds: 1000S - -3rd-svc: - urls: - - rest://localhost:8080?sslEnabled=false&urlPrefix=%2Fapi + retry-success: | + maxAttempts: 2 + retryOnResponseStatus: [503] #########SSL options # open jdk 8 now TLSv1.3 not available diff --git a/demo/demo-springmvc/springmvc-client/src/test/java/org/apache/servicecomb/demo/springmvc/SpringMvcIT.java b/demo/demo-springmvc/springmvc-client/src/test/java/org/apache/servicecomb/demo/springmvc/SpringMvcIT.java index e39f55eb2fb..c445ce1a647 100644 --- a/demo/demo-springmvc/springmvc-client/src/test/java/org/apache/servicecomb/demo/springmvc/SpringMvcIT.java +++ b/demo/demo-springmvc/springmvc-client/src/test/java/org/apache/servicecomb/demo/springmvc/SpringMvcIT.java @@ -28,16 +28,14 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(classes = SpringmvcClient.class) public class SpringMvcIT { - @BeforeEach public void setUp() { TestMgr.errors().clear(); } @Test - public void clientGetsNoError() throws Exception { + public void clientGetsNoError() { SpringmvcClient.run(); - - Assertions.assertTrue(TestMgr.errors().isEmpty()); + Assertions.assertTrue(TestMgr.isSuccess()); } } diff --git a/demo/demo-springmvc/springmvc-server/pom.xml b/demo/demo-springmvc/springmvc-server/pom.xml index 2376724873f..f88876261d6 100644 --- a/demo/demo-springmvc/springmvc-server/pom.xml +++ b/demo/demo-springmvc/springmvc-server/pom.xml @@ -23,40 +23,16 @@ org.apache.servicecomb.demo demo-springmvc - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT springmvc-server Java Chassis::Demo::Spring MVC::Server - - org.apache.servicecomb.demo - demo-schema - - - org.apache.servicecomb - provider-springmvc - - - org.apache.servicecomb - provider-pojo - - - org.apache.servicecomb - swagger-invocation-validator - org.apache.servicecomb metrics-prometheus - - org.apache.servicecomb - config-cc - - - org.apache.servicecomb.demo - demo-signature - @@ -75,28 +51,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcServer.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcServer.java index 3a4fc0985b7..52ede2792d8 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcServer.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcServer.java @@ -17,15 +17,30 @@ package org.apache.servicecomb.demo.springmvc; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; +import org.apache.servicecomb.demo.CategorizedTestCaseRunner; +import org.apache.servicecomb.demo.TestMgr; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class SpringmvcServer { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(SpringmvcServer.class).web(WebApplicationType.NONE).run(args); + + runTests(); + + TestMgr.summary(); + if (!TestMgr.isSuccess()) { + System.exit(1); + } + } + + private static void runTests() { + try { + CategorizedTestCaseRunner.runCategorizedTestCase("springmvc"); + } catch (Exception e) { + TestMgr.failed("runCategorizedTestCase failed", e); + } } } diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/filter/ProviderTestFilter.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/filter/ProviderTestFilter.java index 18520897a8f..310f68e0ca0 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/filter/ProviderTestFilter.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/filter/ProviderTestFilter.java @@ -19,24 +19,21 @@ import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.filter.ProducerFilter; -import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.stereotype.Component; @Component -public class ProviderTestFilter implements ProducerFilter { +public class ProviderTestFilter extends AbstractFilter implements ProviderFilter { @Override - public int getOrder(InvocationType invocationType, String microservice) { - return Filter.PRODUCER_SCHEDULE_FILTER_ORDER - 1800; + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1800; } - @Nonnull @Override public String getName() { return "test-provider"; diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/AnnotationsSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/AnnotationsSchema.java new file mode 100644 index 00000000000..45f2c01e4b3 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/AnnotationsSchema.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.server; + +import org.apache.servicecomb.demo.controller.Person; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.GetMapping; +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 io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.ws.rs.core.MediaType; + +@RestSchema(schemaId = "annotations") +@RequestMapping(path = "/springmvc/annotations", produces = MediaType.APPLICATION_JSON) +public class AnnotationsSchema { + @GetMapping(path = "/add") + public int add(@RequestParam(name = "a", defaultValue = "10") int a, + @RequestParam(name = "b", defaultValue = "10") int b) { + return a + b; + } + + @RequestMapping(path = "/sayhei", method = RequestMethod.GET) + public String sayHei(@RequestHeader(name = "name", defaultValue = "test") String name) { + return "hei " + name; + } + + @GetMapping(path = "/sayhi") + @Parameters({ + @Parameter(name = "name", in = ParameterIn.QUERY, schema = @Schema(type = "string", defaultValue = "test")), + @Parameter(name = "age", in = ParameterIn.QUERY, schema = @Schema(type = "integer", defaultValue = "20")) + }) + public String sayHi(String name, int age) { + return "hi " + name + " your age is : " + age; + } + + @RequestMapping(path = "/saysomething", method = RequestMethod.POST) + public String saySomething(String prefix, @RequestBody(required = false) Person user) { + if (user == null || user.getName() == null || user.getName().isEmpty()) { + return "No user data found"; + } + return prefix + " " + user.getName(); + } + + @RequestMapping(path = "/say", method = RequestMethod.POST) + public String say(@RequestBody(required = false) String user) { + if (user == null || user.isEmpty()) { + return "No user name found"; + } + return user; + } + + @RequestMapping(path = "/testRequiredBody", method = RequestMethod.POST) + public String testRequiredBody(@RequestBody(required = true) Person user) { + if (user == null) { + return "Should not happen"; + } + return user.getName(); + } + + @RequestMapping(path = "/testRegExpPath/{path: .+}", method = RequestMethod.GET) + public String testRegExpPath(@RequestParam("name") String name) { + return name; + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/AnnotationsTest.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/AnnotationsTest.java deleted file mode 100644 index b189ee8947c..00000000000 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/AnnotationsTest.java +++ /dev/null @@ -1,81 +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.servicecomb.demo.springmvc.server; - -import javax.ws.rs.core.MediaType; - -import org.apache.servicecomb.demo.controller.Person; -import org.apache.servicecomb.provider.rest.common.RestSchema; -import org.springframework.web.bind.annotation.GetMapping; -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 io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiParam; - -@RestSchema(schemaId = "annotations") -@RequestMapping(path = "/springmvc/annotations", produces = MediaType.APPLICATION_JSON) -public class AnnotationsTest { - @GetMapping(path = "/add") - public int add(@RequestParam(name = "a", defaultValue = "10") int a, - @RequestParam(name = "b", defaultValue = "10") int b) { - return a + b; - } - - @RequestMapping(path = "/sayhei", method = RequestMethod.GET) - public String sayHei(@RequestHeader(name = "name", defaultValue = "test") String name) { - return "hei " + name; - } - - @GetMapping(path = "/sayhi") - @ApiImplicitParams({ - @ApiImplicitParam(name = "name", paramType = "query", dataType = "string", defaultValue = "test"), - @ApiImplicitParam(name = "age", paramType = "query", dataType = "integer", defaultValue = "20") - }) - public String sayHi(String name, int age) { - return "hi " + name + " your age is : " + age; - } - - @RequestMapping(path = "/saysomething", method = RequestMethod.POST) - public String saySomething(String prefix, @RequestBody(required = false) @ApiParam(required = false) Person user) { - if (user == null || user.getName() == null || user.getName().isEmpty()) { - return "No user data found"; - } - return prefix + " " + user.getName(); - } - - @RequestMapping(path = "/say", method = RequestMethod.POST) - public String say(@RequestBody(required = false) String user) { - if (user == null || user.isEmpty()) { - return "No user name found"; - } - return user; - } - - @RequestMapping(path = "/testRequiredBody", method = RequestMethod.POST) - public String testRequiredBody(@RequestBody(required = true) Person user) { - if (user == null) { - return "Should not happen"; - } - return user.getName(); - } -} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/BigNumberSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/BigNumberSchema.java new file mode 100644 index 00000000000..1190fde43f8 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/BigNumberSchema.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.servicecomb.demo.springmvc.server; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RestSchema(schemaId = "BigNumberSchema") +@RequestMapping("/bigNumber") +public class BigNumberSchema { + @PostMapping(path = "/integer", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public BigInteger bigInteger(@RequestHeader("intHeader") BigInteger intHeader, + @RequestParam("intQuery") BigInteger intQuery, @RequestAttribute("intForm") BigInteger intForm) { + return intHeader.add(intQuery).add(intForm); + } + + @PostMapping(path = "/decimal", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public BigDecimal bigDecimal(@RequestHeader("decimalHeader") BigDecimal decimalHeader, + @RequestParam("decimalQuery") BigDecimal decimalQuery, @RequestAttribute("decimalForm") BigDecimal decimalForm) { + return decimalHeader.add(decimalQuery).add(decimalForm); + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java index 27b8091879a..36f8518afe4 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java @@ -25,15 +25,9 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.Part; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Response.Status; - import org.apache.commons.io.IOUtils; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; -import org.apache.servicecomb.core.BootListener; -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.demo.EmptyObject; import org.apache.servicecomb.demo.Generic; import org.apache.servicecomb.demo.compute.GenericParam; @@ -42,15 +36,13 @@ import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.demo.ignore.InputModelForTestIgnore; import org.apache.servicecomb.demo.ignore.OutputModelForTestIgnore; -import org.apache.servicecomb.demo.jaxbbean.JAXBPerson; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.demo.springmvc.decoderesponse.DecodeTestResponse; import org.apache.servicecomb.foundation.common.Holder; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.metrics.core.MetricsBootListener; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; -import org.apache.servicecomb.swagger.extend.annotations.ResponseHeaders; +import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; @@ -58,6 +50,7 @@ import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -80,13 +73,20 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import io.swagger.annotations.ResponseHeader; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.vertx.core.json.JsonObject; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Response.Status; @RestSchema(schemaId = "codeFirst") @RequestMapping(path = "/codeFirstSpringmvc", produces = MediaType.APPLICATION_JSON_VALUE) @@ -133,21 +133,34 @@ public String fileUpload(@RequestPart(name = "file1") MultipartFile file1, return _fileUpload(file1, file2); } - @ResponseHeaders({@ResponseHeader(name = "h1", response = String.class), - @ResponseHeader(name = "h2", response = String.class)}) + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Date.class)) + , description = "", + headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), + @Header(name = "h2", schema = @Schema(implementation = String.class))}) @RequestMapping(path = "/responseEntity", method = RequestMethod.POST) public ResponseEntity responseEntity(InvocationContext c1, @RequestAttribute("date") Date date) { HttpHeaders headers = new HttpHeaders(); - headers.add("h1", "h1v " + c1.getContext().get(Const.SRC_MICROSERVICE)); + headers.add("h1", "h1v " + c1.getContext().get(CoreConst.SRC_MICROSERVICE)); InvocationContext c2 = ContextUtils.getInvocationContext(); - headers.add("h2", "h2v " + c2.getContext().get(Const.SRC_MICROSERVICE)); + headers.add("h2", "h2v " + c2.getContext().get(CoreConst.SRC_MICROSERVICE)); return new ResponseEntity<>(date, headers, HttpStatus.ACCEPTED); } - @ResponseHeaders({@ResponseHeader(name = "h1", response = String.class), - @ResponseHeader(name = "h2", response = String.class)}) + @GetMapping(value = "/getAuthorization", produces = {"application/json"}) + public String getAuthorization( + @RequestHeader(value = "test") String test, + @RequestParam(value = "param") String param, + @Parameter(description = "Authorization header", required = true, in = ParameterIn.HEADER, + name = "Authorization") @RequestHeader("Authorization") String authorization) { + return test + param + authorization; + } + + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Date.class)) + , description = "", + headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), + @Header(name = "h2", schema = @Schema(implementation = String.class))}) @RequestMapping(path = "/responseEntity", method = RequestMethod.PATCH) public ResponseEntity responseEntityPATCH(InvocationContext c1, @RequestAttribute("date") Date date) { return responseEntity(c1, date); @@ -155,32 +168,34 @@ public ResponseEntity responseEntityPATCH(InvocationContext c1, @RequestAt // This definition is not correct, response type is not // same as the actual one. May be not support in future. - @ApiResponse(code = 200, response = User.class, message = "") - @ResponseHeaders({@ResponseHeader(name = "h1", response = String.class), - @ResponseHeader(name = "h2", response = String.class)}) + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = User.class)) + , description = "", + headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), + @Header(name = "h2", schema = @Schema(implementation = String.class))}) @RequestMapping(path = "/cseResponse", method = RequestMethod.GET) public Response cseResponse(InvocationContext c1) { Response response = Response.createSuccess(Status.ACCEPTED, new User()); - response.addHeader("h1", "h1v " + c1.getContext().get(Const.SRC_MICROSERVICE)); + response.addHeader("h1", "h1v " + c1.getContext().get(CoreConst.SRC_MICROSERVICE)); InvocationContext c2 = ContextUtils.getInvocationContext(); - response.addHeader("h2", "h2v " + c2.getContext().get(Const.SRC_MICROSERVICE)); + response.addHeader("h2", "h2v " + c2.getContext().get(CoreConst.SRC_MICROSERVICE)); return response; } // This definition is correct, but not supported by highway. // highway do not support define code other than 200 - @ApiResponse(code = 202, response = User.class, message = "") - @ResponseHeaders({@ResponseHeader(name = "h1", response = String.class), - @ResponseHeader(name = "h2", response = String.class)}) + @ApiResponse(responseCode = "202", content = @Content(schema = @Schema(implementation = User.class)) + , description = "", + headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), + @Header(name = "h2", schema = @Schema(implementation = String.class))}) @RequestMapping(path = "/cseResponseCorrect", method = RequestMethod.GET) public Response cseResponseCorrect(InvocationContext c1) { Response response = Response.createSuccess(Status.ACCEPTED, new User()); - response.addHeader("h1", "h1v " + c1.getContext().get(Const.SRC_MICROSERVICE)); + response.addHeader("h1", "h1v " + c1.getContext().get(CoreConst.SRC_MICROSERVICE)); InvocationContext c2 = ContextUtils.getInvocationContext(); - response.addHeader("h2", "h2v " + c2.getContext().get(Const.SRC_MICROSERVICE)); + response.addHeader("h2", "h2v " + c2.getContext().get(CoreConst.SRC_MICROSERVICE)); return response; } @@ -195,11 +210,6 @@ public String textPlain(@RequestBody String body) { return body; } - @RequestMapping(path = "/appXml", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_XML_VALUE) - public JAXBPerson appXml(@RequestBody JAXBPerson person) { - return person; - } - @RequestMapping(path = "/bytes", method = RequestMethod.POST) public byte[] bytes(@RequestBody byte[] input) { input[0] = (byte) (input[0] + 1); @@ -212,7 +222,7 @@ public Date addDate(@RequestAttribute("date") Date date, @QueryParam("seconds") } // this should be ignored as it's hidden - @ApiOperation(value = "", hidden = true, httpMethod = "POST") + @Operation(summary = "", hidden = true, method = "POST") public int add(@RequestParam("a") int a) { return a; } @@ -223,7 +233,7 @@ public int add(@RequestAttribute("a") int a, @RequestAttribute("b") int b) { } @GetMapping(path = "/reduce") - @ApiImplicitParams({@ApiImplicitParam(name = "a", dataType = "integer", format = "int32", paramType = "query")}) + @Parameters({@Parameter(name = "a", schema = @Schema(implementation = int.class), in = ParameterIn.QUERY)}) public int reduce(HttpServletRequest request, @CookieValue(name = "b") int b) { int a = Integer.parseInt(request.getParameter("a")); return a - b; @@ -240,7 +250,8 @@ public Person sayHello(@RequestBody Person user) { public String testRawJsonString(String jsonInput) { Map person; try { - person = RestObjectMapperFactory.getRestObjectMapper().readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); + person = RestObjectMapperFactory.getRestObjectMapper() + .readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); } catch (Exception e) { e.printStackTrace(); return null; @@ -291,10 +302,11 @@ public String addString(@RequestParam(name = "s") List s) { } // Using 490, 590 error code, the response type should be CommonExceptionData. Or we need - // complex ExceptionConverters to deal with exceptions thrown by Hanlders, etc. + // complex ExceptionConverters to deal with exceptions thrown by Handlers, etc. @RequestMapping(path = "/fallback/returnnull/{name}", method = RequestMethod.GET) - @ApiResponses(value = {@ApiResponse(code = 200, response = String.class, message = "xxx"), - @ApiResponse(code = 490, response = CommonExceptionData.class, message = "xxx")}) + @ApiResponses(value = {@ApiResponse(responseCode = "200", + content = @Content(schema = @Schema(implementation = String.class)), description = "xxx"), + @ApiResponse(responseCode = "490", content = @Content(schema = @Schema(implementation = CommonExceptionData.class)), description = "xxx")}) public String fallbackReturnNull(@PathVariable(name = "name") String name) { if ("throwexception".equals(name)) { throw new InvocationException(490, "490", new CommonExceptionData("xxx")); @@ -303,8 +315,11 @@ public String fallbackReturnNull(@PathVariable(name = "name") String name) { } @RequestMapping(path = "/fallback/throwexception/{name}", method = RequestMethod.GET) - @ApiResponses(value = {@ApiResponse(code = 200, response = String.class, message = "xxx"), - @ApiResponse(code = 490, response = CommonExceptionData.class, message = "xxx")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)) + , description = "xxx"), + @ApiResponse(responseCode = "490", content = @Content(schema = @Schema(implementation = CommonExceptionData.class)) + , description = "xxx")}) public String fallbackThrowException(@PathVariable(name = "name") String name) { if ("throwexception".equals(name)) { throw new InvocationException(490, "490", new CommonExceptionData("xxx")); @@ -313,8 +328,11 @@ public String fallbackThrowException(@PathVariable(name = "name") String name) { } @RequestMapping(path = "/fallback/fromcache/{name}", method = RequestMethod.GET) - @ApiResponses(value = {@ApiResponse(code = 200, response = String.class, message = "xxx"), - @ApiResponse(code = 490, response = CommonExceptionData.class, message = "xxx")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)) + , description = "xxx"), + @ApiResponse(responseCode = "490", content = @Content(schema = @Schema(implementation = CommonExceptionData.class)) + , description = "xxx")}) public String fallbackFromCache(@PathVariable(name = "name") String name) { if ("throwexception".equals(name)) { throw new InvocationException(490, "490", new CommonExceptionData("xxx")); @@ -323,8 +341,11 @@ public String fallbackFromCache(@PathVariable(name = "name") String name) { } @RequestMapping(path = "/fallback/force/{name}", method = RequestMethod.GET) - @ApiResponses(value = {@ApiResponse(code = 200, response = String.class, message = "xxx"), - @ApiResponse(code = 490, response = CommonExceptionData.class, message = "xxx")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)) + , description = "xxx"), + @ApiResponse(responseCode = "490", content = @Content(schema = @Schema(implementation = CommonExceptionData.class)) + , description = "xxx")}) public String fallbackForce(@PathVariable(name = "name") String name) { if ("throwexception".equals(name)) { throw new InvocationException(490, "490", new CommonExceptionData("xxx")); @@ -338,8 +359,11 @@ public enum NameType { } @RequestMapping(path = "/testenum/{name}", method = RequestMethod.GET) - @ApiResponses(value = {@ApiResponse(code = 200, response = String.class, message = "200 normal"), - @ApiResponse(code = 490, response = CommonExceptionData.class, message = "490 exception")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)) + , description = "xxx"), + @ApiResponse(responseCode = "490", content = @Content(schema = @Schema(implementation = CommonExceptionData.class)) + , description = "xxx")}) public String testEnum(@RequestParam(name = "username") String username, @PathVariable(value = "name") NameType nameType) { return nameType.toString(); @@ -360,7 +384,8 @@ public OutputModelForTestIgnore testModelWithIgnoreField(@RequestBody InputModel public String testRawJsonAnnotation(@RawJsonRequestBody String jsonInput) { Map person; try { - person = RestObjectMapperFactory.getRestObjectMapper().readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); + person = RestObjectMapperFactory.getRestObjectMapper() + .readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); } catch (Exception e) { e.printStackTrace(); return null; @@ -369,11 +394,13 @@ public String testRawJsonAnnotation(@RawJsonRequestBody String jsonInput) { } @PostMapping(path = "/testform") - @ApiImplicitParams({ - @ApiImplicitParam(name = "form1", dataType = "string", paramType = "form", value = "a required form param", - required = true), - @ApiImplicitParam(name = "form2", dataType = "string", paramType = "form", value = "an optional form param", - required = false)}) + @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = {@Content(mediaType = SwaggerConst.FORM_MEDIA_TYPE, + schema = @Schema(name = "form1", implementation = String.class, + nullable = false, description = "a required form param")), + @Content(mediaType = SwaggerConst.FORM_MEDIA_TYPE, + schema = @Schema(name = "form2", implementation = String.class, + nullable = true, description = "an optional form param"))}) public String testform(HttpServletRequest request) { String form1 = request.getParameter("form1"); String form2 = request.getParameter("form2"); @@ -381,8 +408,8 @@ public String testform(HttpServletRequest request) { return form1 + form2; } - MetricsBootListener metricsBootListener = SPIServiceUtils - .getTargetService(BootListener.class, MetricsBootListener.class); + @Autowired + MetricsBootListener metricsBootListener; //Only for Prometheus integration test @RequestMapping(path = "/prometheusForTest", method = RequestMethod.GET) @@ -396,7 +423,7 @@ public String prometheusForTest() { @GetMapping(path = "/traceId") public String getTraceId() { - return ContextUtils.getInvocationContext().getContext(Const.TRACE_ID_NAME); + return ContextUtils.getInvocationContext().getContext(CoreConst.TRACE_ID_NAME); } @PostMapping(path = "/emptyObject") @@ -468,7 +495,7 @@ public Generic> testGenericGenericUser(@RequestBody Generic deleteAfterFinished(@RequestParam("content") String content) throws IOException { - File file = createTempFile(content); + public ResponseEntity deleteAfterFinished(@RequestParam("content") String content, + @RequestParam(value = "fileName", required = false) String fileName) throws IOException { + File file; + + if (StringUtils.isNotEmpty(fileName)) { + file = createTempFile(fileName, content); + } else { + file = createTempFile(content); + } return ResponseEntity .ok() @@ -92,8 +99,46 @@ public ResponseEntity notDeleteAfterFinished(@RequestParam("content") Stri .body(new FilePart(null, file)); } + @GetMapping(path = "/setContentTypeByResponseEntity") + public ResponseEntity setContentTypeByResponseEntity(@RequestParam("content") String content, + @RequestParam("contentType") String contentType) throws IOException { + File file = createTempFile(content); + + return ResponseEntity + .ok() + .header(HttpHeaders.CONTENT_TYPE, contentType) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") + .body(new FilePart(null, file)); + } + @GetMapping(path = "/assertLastFileDeleted") public boolean assertLastFileDeleted() { return lastFile.exists(); } + + @GetMapping(path = "/testResponseOKExceptionDownload") + public ResponseEntity testResponseOKExceptionDownload( + @RequestParam("exception") boolean exception, + @RequestParam("content") String content, + @RequestParam("contentType") String contentType) throws IOException { + if (exception) { + throw new ResponseOKException(); + } + + File file = createTempFile(content); + + return ResponseEntity + .ok() + .header(HttpHeaders.CONTENT_TYPE, contentType) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") + .body(new FilePart(null, file)); + } + + @GetMapping(path = "/testResponseOKExceptionBean") + public boolean testResponseOKExceptionBean(@RequestParam("exception") boolean exception) { + if (exception) { + throw new ResponseOKException(); + } + return true; + } } diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java index ebbe1dd8ccc..ddf56fb5a3f 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java @@ -21,17 +21,16 @@ import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.registry.RegistrationManager; +import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; -import com.netflix.config.DynamicPropertyFactory; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectWriter; -import io.swagger.models.Swagger; -import io.swagger.util.Yaml; +import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.oas.models.OpenAPI; /** * Testing after bootup. @@ -42,62 +41,22 @@ public class ProducerTestsAfterBootup implements BootListener { private ObjectWriter writer = Yaml.pretty(); - private static final String EXPECTED_DATA = "---\n" - + "swagger: \"2.0\"\n" - + "info:\n" - + " version: \"1.0.0\"\n" - + " title: \"swagger definition for org.apache.servicecomb.demo.springmvc.server.CodeFirstSpringmvcForSchema\"\n" - + " x-java-interface: \"gen.swagger.CodeFirstSpringmvcForSchemaIntf\"\n" - + "basePath: \"/forScheam\"\n" - + "consumes:\n" - + "- \"application/json\"\n" - + "produces:\n" - + "- \"application/json\"\n" - + "paths:\n" - + " /uploadFile:\n" - + " post:\n" - + " operationId: \"uploadAwardFile\"\n" - + " consumes:\n" - + " - \"multipart/form-data\"\n" - + " produces:\n" - + " - \"application/json\"\n" - + " parameters:\n" - + " - name: \"fileType\"\n" - + " in: \"query\"\n" - + " required: true\n" - + " type: \"string\"\n" - + " - name: \"zoneId\"\n" - + " in: \"query\"\n" - + " required: true\n" - + " type: \"string\"\n" - + " - name: \"file\"\n" - + " in: \"formData\"\n" - + " required: true\n" - + " type: \"file\"\n" - + " responses:\n" - + " \"200\":\n" - + " description: \"response of 200\"\n" - + " schema:\n" - + " type: \"boolean\"\n"; - public void testSchemaNotChange(SCBEngine scbEngine) { LOGGER.info("ProducerTestsAfterBootup testing start"); - //we can not set microserviceName any more - SchemaMeta meta = scbEngine.getProducerProviderManager().registerSchema("test1", new CodeFirstSpringmvcForSchema()); + SchemaMeta meta = scbEngine.getProducerMicroserviceMeta().findSchemaMeta("CodeFirstSpringmvcForSchema"); String codeFirst = getSwaggerContent(meta.getSwagger()); - TestMgr.check(EXPECTED_DATA, - codeFirst); - } - public void testRegisteredBasePath() { - if (DynamicPropertyFactory.getInstance().getBooleanProperty("servicecomb.test.vert.transport", true).get()) { - TestMgr.check(21, RegistrationManager.INSTANCE.getMicroservice().getPaths().size()); - } else { - TestMgr.check(22, RegistrationManager.INSTANCE.getMicroservice().getPaths().size()); + String expectSchema = UnitTestSwaggerUtils.loadExpect("schemas/CodeFirstSpringmvcForSchema.yaml") + .replace("\r\n", "\n").trim(); + int offset = expectSchema.indexOf("---\nopenapi: 3.0.1"); + if (offset > 0) { + expectSchema = expectSchema.substring(offset + 4); } + + TestMgr.check(expectSchema.trim(), codeFirst.trim()); } - private String getSwaggerContent(Swagger swagger) { + private String getSwaggerContent(OpenAPI swagger) { try { return writer.writeValueAsString(swagger); } catch (JsonProcessingException e) { @@ -109,7 +68,6 @@ private String getSwaggerContent(Swagger swagger) { public void onBootEvent(BootEvent event) { if (event.getEventType() == BootListener.EventType.AFTER_REGISTRY) { testSchemaNotChange(event.getScbEngine()); - testRegisteredBasePath(); if (!TestMgr.isSuccess()) { TestMgr.summary(); throw new IllegalStateException("some tests are failed. "); @@ -117,4 +75,3 @@ public void onBootEvent(BootEvent event) { } } } - diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKData.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKData.java new file mode 100644 index 00000000000..aeadbbeda90 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKData.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.servicecomb.demo.springmvc.server; + +public class ResponseOKData { + private String errorCode; + + private String errorMessage; + + public ResponseOKData(String errorCode, String errorMessage) { + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKException.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKException.java new file mode 100644 index 00000000000..6dcafb2db99 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKException.java @@ -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. + */ +package org.apache.servicecomb.demo.springmvc.server; + +public class ResponseOKException extends RuntimeException { +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKExceptionConverter.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKExceptionConverter.java new file mode 100644 index 00000000000..7358b72171c --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKExceptionConverter.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.springmvc.server; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.exception.ExceptionConverter; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.StatusType; + +public class ResponseOKExceptionConverter implements + ExceptionConverter { + + @Override + public boolean canConvert(Throwable throwable) { + return throwable instanceof ResponseOKException; + } + + @Override + public InvocationException convert(Invocation invocation, ResponseOKException throwable, StatusType genericStatus) { + // This is for compatible usage. For best practise, any status code + // should have only one type of response. + return new InvocationException(Status.OK, new ResponseOKData("code-005", "error-005")); + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RestProducersCustomized.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RestProducersCustomized.java new file mode 100644 index 00000000000..a3858541825 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RestProducersCustomized.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.server; + +import org.apache.servicecomb.provider.rest.common.RestProducers; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +@Component +@Primary +// This example shows user's how to customize RestProducers by @Primary annotation +public class RestProducersCustomized extends RestProducers { +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RestProducersCutomized.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RestProducersCutomized.java deleted file mode 100644 index 445c6a35b07..00000000000 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RestProducersCutomized.java +++ /dev/null @@ -1,28 +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.servicecomb.demo.springmvc.server; - -import org.apache.servicecomb.provider.rest.common.RestProducers; -import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Component; - -@Component -@Primary -// This example shows user's how to customize RestProducers by @Primary annotation -public class RestProducersCutomized extends RestProducers { -} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RetrySchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RetrySchema.java index 110176002f4..b3a32b34020 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RetrySchema.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RetrySchema.java @@ -19,7 +19,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicLong; -import javax.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SchemeInterfaceSpringmvc.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SchemeInterfaceSpringmvc.java index 15e28fb32b2..8d4b02e85b5 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SchemeInterfaceSpringmvc.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SchemeInterfaceSpringmvc.java @@ -17,15 +17,22 @@ package org.apache.servicecomb.demo.springmvc.server; -import javax.validation.constraints.Min; -import javax.ws.rs.core.MediaType; - import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.constraints.Min; +import jakarta.ws.rs.core.MediaType; + @RequestMapping(path = "/springmvc/schemaInterface", produces = MediaType.APPLICATION_JSON) public interface SchemeInterfaceSpringmvc { @GetMapping(path = "/add") int add(@Min(1) @RequestParam("a") int a, @Min(1) @RequestParam("b") int b); + + @GetMapping(path = "/tailingSlash/") + String tailingSlash(HttpServletRequest request, @Min(1) @RequestParam("a") int a, @Min(1) @RequestParam("b") int b); + + @GetMapping(path = "/nonTailingSlash") + String nonTailingSlash(HttpServletRequest request, @Min(1) @RequestParam("a") int a, @Min(1) @RequestParam("b") int b); } diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SchemeInterfaceSpringmvcImpl.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SchemeInterfaceSpringmvcImpl.java index 548e59197c1..828a9fc160d 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SchemeInterfaceSpringmvcImpl.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SchemeInterfaceSpringmvcImpl.java @@ -17,10 +17,11 @@ package org.apache.servicecomb.demo.springmvc.server; -import javax.validation.constraints.Min; - import org.apache.servicecomb.provider.rest.common.RestSchema; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.constraints.Min; + @RestSchema(schemaId = "SchemeInterfaceSpringmvc", schemaInterface = SchemeInterfaceSpringmvc.class) public class SchemeInterfaceSpringmvcImpl implements SchemeInterfaceSpringmvc { @Override @@ -28,6 +29,26 @@ public int add(@Min(1) int a, @Min(1) int b) { return a + b; } + @Override + public String tailingSlash(HttpServletRequest request, int a, int b) { + StringBuilder sb = new StringBuilder(); + sb.append(request.getRequestURI()).append(";") + .append(request.getRequestURL()).append(";") + .append(request.getPathInfo()).append(";") + .append(a - b); + return sb.toString(); + } + + @Override + public String nonTailingSlash(HttpServletRequest request, int a, int b) { + StringBuilder sb = new StringBuilder(); + sb.append(request.getRequestURI()).append(";") + .append(request.getRequestURL()).append(";") + .append(request.getPathInfo()).append(";") + .append(a - b); + return sb.toString(); + } + public int reduce(int a, int b) { return a - b; } diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SpringMVCCommonSchemaInterface.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SpringMVCCommonSchemaInterface.java index edd200bd9ce..1bd1abba2e3 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SpringMVCCommonSchemaInterface.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SpringMVCCommonSchemaInterface.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.demo.springmvc.server; -import javax.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.demo.CommonSchemaInterface; diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SpringMvcDefaultValues.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SpringMvcDefaultValues.java index 3e915096205..a30b9ad84d3 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SpringMvcDefaultValues.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SpringMvcDefaultValues.java @@ -16,9 +16,9 @@ */ package org.apache.servicecomb.demo.springmvc.server; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.ws.rs.core.MediaType; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/TransportSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/TransportSchema.java new file mode 100644 index 00000000000..158cd806188 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/TransportSchema.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.servicecomb.demo.springmvc.server; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "TransportSchema") +@RequestMapping(path = "/transport") +public class TransportSchema { + @GetMapping(path = "/restTransport") + @Transport(name = CoreConst.RESTFUL) + public boolean restTransport(InvocationContext invocation) { + return CoreConst.RESTFUL.equals(((Invocation) invocation).getTransportName()); + } + + @GetMapping(path = "/highwayTransport") + @Transport(name = CoreConst.HIGHWAY) + public boolean highwayTransport(InvocationContext invocation) { + return CoreConst.HIGHWAY.equals(((Invocation) invocation).getTransportName()); + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/UploadSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/UploadSchema.java index 494a4d663df..1a3a737d592 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/UploadSchema.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/UploadSchema.java @@ -78,6 +78,21 @@ public String uploadFileRequestPartAttribute(@RequestPart(name = "file") Multipa } } + @PostMapping(path = "/uploadFileRequestPartAttributeList", produces = MediaType.TEXT_PLAIN_VALUE) + public String uploadFileRequestPartAttributeList(@RequestPart(name = "files") List files, + @RequestPart(name = "attributes") List attributes) throws IOException { + StringBuilder result = new StringBuilder(); + for (MultipartFile file : files) { + try (InputStream is = file.getInputStream()) { + result.append(IOUtils.toString(is, StandardCharsets.UTF_8)); + } + } + for (String attribute : attributes) { + result.append(attribute); + } + return result.toString(); + } + @PostMapping(path = "/uploadFileAndAttribute", produces = MediaType.TEXT_PLAIN_VALUE) public String uploadFileAndAttribute(@RequestPart(name = "file") MultipartFile file, @RequestAttribute(name = "attribute") String attribute) throws IOException { diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/WeakSpringmvc.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/WeakSpringmvc.java index 834072175e9..b9d6a119817 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/WeakSpringmvc.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/WeakSpringmvc.java @@ -28,33 +28,32 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; @RestSchema(schemaId = "weakSpringmvc") @RequestMapping(path = "/weakSpringmvc", produces = MediaType.APPLICATION_JSON_VALUE) public class WeakSpringmvc { @GetMapping(path = "/diffNames") - @ApiOperation(value = "differentName", nickname = "differentName") + @Operation(summary = "differentName", operationId = "differentName") public int diffNames(@RequestParam("x") int a, @RequestParam("y") int b) { return a * 2 + b; } @GetMapping(path = "/genericParams") - @ApiOperation(value = "genericParams", nickname = "genericParams") + @Operation(summary = "genericParams", operationId = "genericParams") public List> genericParams(@RequestParam("code") int code, @RequestBody List> names) { return names; } @GetMapping(path = "/genericParamsModel") - @ApiOperation(value = "genericParamsModel", nickname = "genericParamsModel") + @Operation(summary = "genericParamsModel", operationId = "genericParamsModel") public GenericsModel genericParamsModel(@RequestParam("code") int code, @RequestBody GenericsModel model) { return model; } @GetMapping(path = "/specialNameModel") - @ApiOperation(value = "specialNameModel", nickname = "specialNameModel") + @Operation(summary = "specialNameModel", operationId = "specialNameModel") public SpecialNameModel specialNameModel(@RequestParam("code") int code, @RequestBody SpecialNameModel model) { return model; } - } diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/EarlyConsumer.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/EarlyConsumer.java index 09cd40ed5b6..79b89668560 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/EarlyConsumer.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/EarlyConsumer.java @@ -45,7 +45,7 @@ public EarlyConsumer() { // ignore error } try { - Thread.sleep(100); + Thread.sleep(500); } catch (Throwable e) { // ignore error } diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/Register.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/Register.java index aa2fab084ef..cc9144fe617 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/Register.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/Register.java @@ -17,26 +17,37 @@ package org.apache.servicecomb.demo.springmvc.third; -import java.util.Arrays; +import java.util.List; -import org.apache.servicecomb.provider.pojo.registry.ThirdServiceWithInvokerRegister; -import org.springframework.stereotype.Component; - -import com.netflix.config.DynamicPropertyFactory; +import org.apache.servicecomb.localregistry.RegistryBean; +import org.apache.servicecomb.localregistry.RegistryBean.Instance; +import org.apache.servicecomb.localregistry.RegistryBean.Instances; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; /** * see: https://github.com/apache/servicecomb-java-chassis/issues/2534 */ -@Component -public class Register extends ThirdServiceWithInvokerRegister { - public Register() { - super("third"); - addSchema("heartbeat", HealthSchema.class); - if (DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.test.vert.transport", true).get()) { - setUrls("", Arrays.asList("rest://localhost:8080?sslEnabled=false&urlPrefix=%2Fapi")); +@Configuration +public class Register { + @Autowired + Environment environment; + + @Bean + public RegistryBean thirdRegistryBean() { + String endpoint; + if (environment.getProperty("servicecomb.test.vert.transport", boolean.class, true)) { + endpoint = "rest://localhost:8080?sslEnabled=false&urlPrefix=%2Fapi"; } else { - setUrls("", Arrays.asList("rest://localhost:8080?sslEnabled=false")); + endpoint = "rest://localhost:8080?sslEnabled=false"; } + + return new RegistryBean().addSchemaInterface("heartbeat", HealthSchema.class) + .setAppId("springmvctest") + .setServiceName("third") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of(new Instance().setEndpoints(List.of(endpoint))))); } } diff --git a/demo/demo-springmvc/springmvc-server/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter b/demo/demo-springmvc/springmvc-server/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter new file mode 100644 index 00000000000..f25ea28e1c1 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter @@ -0,0 +1,18 @@ +# +# 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.servicecomb.demo.springmvc.server.ResponseOKExceptionConverter diff --git a/demo/demo-springmvc/springmvc-server/src/main/resources/microservice.yaml b/demo/demo-springmvc/springmvc-server/src/main/resources/microservice.yaml index f26e1d26dc8..c5c10c5b41b 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/resources/microservice.yaml +++ b/demo/demo-springmvc/springmvc-server/src/main/resources/microservice.yaml @@ -15,35 +15,24 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -APPLICATION_ID: springmvctest +# test not supported configuration +# service_description.initialStatus service_description: - name: springmvc - version: 0.0.3 - paths: - - path: /test1/testpath - property: - checksession: false - - path: /test2/testpath - property: - checksession: true -instance_description: initialStatus: TESTING + servicecomb: service: - registry: - registerPath: true + application: springmvctest + name: springmvc + version: 0.0.3 + + registry: + sc: address: http://127.0.0.1:9980,http://127.0.0.1:30100 - client: - timeout: - idle: 60 - watch: 60 - instance: - healthCheck: - interval: 3 - pull: - interval: 10 - watch: true autodiscovery: true + healthCheckIntervalInSeconds: 3 + pollIntervalInMillis: 10000 + watch: true uploads: directory: target @@ -84,6 +73,11 @@ servicecomb: region: my-Region availableZone: my-Zone codec.printErrorMessage: true + + # For old testing + loadbalance: + isolation: + errorThresholdPercentage: 20 #########SSL options # open jdk 8 now TLSv1.3 not available # ssl.protocols: TLSv1.3 @@ -103,4 +97,3 @@ ssl.crl: revoke.crl ssl.sslCustomClass: org.apache.servicecomb.demo.DemoSSLCustom vertx.disableFileCPResolving: false # false: create the .vertx directory, true: do not create - diff --git a/demo/demo-springmvc/springmvc-server/src/main/resources/schemas/CodeFirstSpringmvcForSchema.yaml b/demo/demo-springmvc/springmvc-server/src/main/resources/schemas/CodeFirstSpringmvcForSchema.yaml new file mode 100644 index 00000000000..2263730b56a --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/resources/schemas/CodeFirstSpringmvcForSchema.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.demo.springmvc.server.CodeFirstSpringmvcForSchema + version: 1.0.0 +servers: +- url: /forSchema +paths: + /uploadFile: + post: + operationId: uploadAwardFile + parameters: + - name: fileType + in: query + required: true + schema: + type: string + - name: zoneId + in: query + required: true + schema: + type: string + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: boolean +components: {} diff --git a/demo/demo-zeroconfig-registry/README.md b/demo/demo-zeroconfig-registry/README.md new file mode 100644 index 00000000000..c2b53cf780c --- /dev/null +++ b/demo/demo-zeroconfig-registry/README.md @@ -0,0 +1,21 @@ +# Introduction + +This demo tests zeroconfig. + +* server + + run in docker + +* client + + run in docker, and find server instances through zeroconfig, find schemas through + schema discovery + +* edge + run in docker, and find server instances through zeroconfig, find schemas through + schema discovery + +* tests + + integration tests run in host machine, find client/edge instances through local, + and schemas from schema discovery. diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/pom.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/pom.xml new file mode 100644 index 00000000000..385bb4950a5 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/pom.xml @@ -0,0 +1,113 @@ + + + + + 4.0.0 + demo-zeroconfig-registry-client + Java Chassis::Demo::ZeroConfig Registry Client + + org.apache.servicecomb.demo + demo-zeroconfig-registry + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.zeroconfig.client.ClientApplication + + + + jakarta.ws.rs + jakarta.ws.rs-api + + + org.apache.servicecomb + java-chassis-spring-boot-starter-servlet + + + org.apache.servicecomb + registry-zero-config + + + org.apache.servicecomb.demo + demo-schema + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientApplication.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientApplication.java similarity index 93% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientApplication.java rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientApplication.java index ba808890523..eaea52f6aa8 100644 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientApplication.java +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientApplication.java @@ -17,13 +17,11 @@ package org.apache.servicecomb.demo.zeroconfig.client; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class ClientApplication { public static void main(final String[] args) throws Exception { diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientModel.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientModel.java similarity index 100% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientModel.java rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientModel.java diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientServerEndpoint.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientServerEndpoint.java new file mode 100644 index 00000000000..a2eb8f0ecb7 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientServerEndpoint.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.servicecomb.demo.zeroconfig.client; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.registry.DiscoveryManager; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +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.RequestParam; + +import io.vertx.core.json.JsonObject; +import jakarta.ws.rs.core.MediaType; + +@RestSchema(schemaId = "ClientServerEndpoint") +@RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) +public class ClientServerEndpoint { + @RpcReference(microserviceName = "demo-zeroconfig-registry-server", schemaId = "ServerEndpoint") + private IServerEndpoint serverEndpoint; + + @RpcReference(microserviceName = "demo-zeroconfig-registry-server", schemaId = "RpcEndpoint") + private IRpcEndpoint rpcEndpoint; + + private DiscoveryManager discoveryManager; + + @Autowired + public void setDiscoveryManager(DiscoveryManager discoveryManager) { + this.discoveryManager = discoveryManager; + } + + @GetMapping(path = "/getName") + public String getName(@RequestParam(name = "name") String name) { + return serverEndpoint.getName(name); + } + + @GetMapping(path = "/getRegisteredMicroservice") + public Set getRegisteredMicroservice() { + boolean result = true; + List microserviceList = discoveryManager + .findServiceInstances("demo-zeroconfig-registry", + "demo-zeroconfig-registry-client"); + if (microserviceList.size() != 2) { // local and zero config both have one instance + result = false; + } + microserviceList = discoveryManager + .findServiceInstances("demo-zeroconfig-registry", + "demo-zeroconfig-registry-server"); + if (microserviceList.size() != 1) { + result = false; + } + + if (result) { + Set names = new HashSet<>(); + names.add("demo-zeroconfig-registry-client"); + names.add("demo-zeroconfig-registry-server"); + return names; + } + + return Collections.emptySet(); + } + + @PostMapping(path = "/jsonObject") + public JsonObject jsonObject(@RequestBody JsonObject jsonObject) { + JsonObject param = new JsonObject(); + param.put("map", jsonObject); + JsonObject message = rpcEndpoint.getJsonObject(param); + JsonObject inner = new JsonObject(); + inner.put("map", message); + return inner; + } + + @PostMapping(path = "/getString") + public String getString(@RawJsonRequestBody String jsonString) { + return jsonString; + } + + @PostMapping(path = "/postModel") + public ClientModel postModel(@RequestBody ClientModel clientModel) { + return clientModel; + } + + @GetMapping(path = "/contextMapper") + public String contextMapper(@RequestHeader("gatewayHeader") String gatewayHeader, + @RequestHeader("clientHeader") String clientHeader, + @RequestParam("gatewayQuery") String gatewayQuery, + @RequestParam("clientQuery") String clientQuery) { + InvocationContext context = ContextUtils.getInvocationContext(); + if (gatewayHeader.equals(context.getContext("context-gateway-header")) && + clientHeader.equals(context.getContext("context-client-header")) && + gatewayQuery.equals(context.getContext("context-gateway-query")) && + clientQuery.equals(context.getContext("context-client-query"))) { + return "success"; + } + return "fail"; + } +} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/GovernanceEndpoint.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/GovernanceEndpoint.java new file mode 100644 index 00000000000..574e0a4f605 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/GovernanceEndpoint.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.servicecomb.demo.zeroconfig.client; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.client.RestOperations; + +import jakarta.ws.rs.core.Response.Status; + +@RestSchema(schemaId = "GovernanceEndpoint") +@RequestMapping("/governance") +public class GovernanceEndpoint { + private static final String SERVER = "servicecomb://demo-zeroconfig-registry-server"; + + private RestOperations restTemplate = RestTemplateBuilder.create(); + + private int count = 0; + + @GetMapping("/hello") + public String hello() { + return restTemplate.getForObject(SERVER + "/governance/hello", String.class); + } + + @GetMapping("/helloRpc") + public String helloRpc() { + return restTemplate.getForObject(SERVER + "/governance/hello", String.class); + } + + @GetMapping("/noPrefixRetry") + public String noPrefixRetry(@RequestParam(name = "invocationID") String invocationID) { + return restTemplate + .getForObject(SERVER + "/noPrefixRetry?invocationID={1}", String.class, + invocationID); + } + + @GetMapping("/retry") + public String retry(@RequestParam(name = "invocationID") String invocationID) { + return restTemplate + .getForObject(SERVER + "/governance/retry?invocationID={1}", String.class, + invocationID); + } + + @GetMapping("/retryRpc") + public String retryRpc(@RequestParam(name = "invocationID") String invocationID) { + return restTemplate + .getForObject(SERVER + "/governance/retryRpc?invocationID={1}", String.class, + invocationID); + } + + @GetMapping("/circuitBreaker") + public String circuitBreaker() throws Exception { + count++; + if (count % 3 == 0) { + return "ok"; + } + throw new InvocationException(Status.SERVICE_UNAVAILABLE, "test error"); + } + + @GetMapping("/bulkhead") + public String bulkhead() { + return restTemplate.getForObject(SERVER + "/governance/hello", String.class); + } +} diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/IRpcEndpoint.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/IRpcEndpoint.java similarity index 100% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/IRpcEndpoint.java rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/IRpcEndpoint.java diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/IServerEndpoint.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/IServerEndpoint.java similarity index 100% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/IServerEndpoint.java rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/IServerEndpoint.java diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/resources/application.yml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/resources/application.yml new file mode 100644 index 00000000000..af6c2946515 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/resources/application.yml @@ -0,0 +1,109 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +server: + port: 8082 + +servicecomb: + service: + application: demo-zeroconfig-registry + name: demo-zeroconfig-registry-client + version: 0.0.2 + rest: + address: 0.0.0.0:8082 + highway: + address: 0.0.0.0:8084 + config: + client: + # for testing dynamic configuration + # serverUri: http://127.0.0.1:30113 + refreshMode: 0 + # refresh_interval: 5000 + # refreshPort: 30114 + governance: + GovernanceEndpoint.helloRpc: + matchType: rpc + demo-zeroconfig-registry-server.GovernanceEndpoint.retryRpc: + matchType: rpc + matchGroup: + demo-rateLimiting: | + matches: + - apiPath: + exact: "/governance/hello" + demo-rateLimiting-rpc: | + matches: + - apiPath: + exact: "GovernanceEndpoint.helloRpc" + demo-retry: | + matches: + - apiPath: + exact: "/governance/retry" + demo-retry-no-prefix: | + matches: + - apiPath: + exact: "/noPrefixRetry" + demo-retry-rpc: | + matches: + - apiPath: + exact: "demo-zeroconfig-registry-server.GovernanceEndpoint.retryRpc" + demo-circuitBreaker: | + matches: + - apiPath: + exact: "/governance/circuitBreaker" + demo-bulkhead: | + matches: + - apiPath: + exact: "/governance/bulkhead" + rateLimiting: + demo-rateLimiting: | + rate: 10 + ## services is optional in configuration file + services: demo-zeroconfig-registry-client + demo-rateLimiting-rpc: | + rate: 10 + ## services is optional in configuration file + services: demo-zeroconfig-registry-client + retry: + demo-retry: | + maxAttempts: 3 + ## services is optional in configuration file + services: demo-zeroconfig-registry-client + demo-retry-no-prefix: | + maxAttempts: 3 + ## services is optional in configuration file + services: demo-zeroconfig-registry-client + demo-retry-rpc: | + maxAttempts: 3 + ## services is optional in configuration file + services: demo-zeroconfig-registry-client + circuitBreaker: + demo-circuitBreaker: | + minimumNumberOfCalls: 10 + slidingWindowSize: 10 + failureRateThreshold: 20 + ## services is optional in configuration file + services: demo-zeroconfig-registry-client + bulkhead: + demo-bulkhead: | + maxConcurrentCalls: 5 + ## services is optional in configuration file + services: demo-zeroconfig-registry-client + context: + headerContextMapper: | + clientHeader: context-client-header + queryContextMapper: | + clientQuery: context-client-query diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/resources/log4j2.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/resources/log4j2.xml similarity index 100% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/resources/log4j2.xml rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/resources/log4j2.xml diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/pom.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/pom.xml new file mode 100644 index 00000000000..c8dcb06893a --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/pom.xml @@ -0,0 +1,118 @@ + + + + + 4.0.0 + demo-zeroconfig-registry-edge + Java Chassis::Demo::ZeroConfig Registry Edge + + org.apache.servicecomb.demo + demo-zeroconfig-registry + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.zeroconfig.edge.EdgeApplication + + + + jakarta.ws.rs + jakarta.ws.rs-api + + + org.apache.servicecomb + edge-core + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + registry-zero-config + + + org.apache.servicecomb.demo + demo-schema + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/EdgeApplication.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/EdgeApplication.java similarity index 95% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/EdgeApplication.java rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/EdgeApplication.java index 6e15633ecf8..6ba5795c2a8 100644 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/EdgeApplication.java +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/EdgeApplication.java @@ -21,13 +21,11 @@ import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication -@EnableServiceComb public class EdgeApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(EdgeApplication.class).web(WebApplicationType.NONE).build().run(args); diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/SelfServiceInvoker.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/SelfServiceInvoker.java new file mode 100644 index 00000000000..e04576d2965 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/SelfServiceInvoker.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.servicecomb.demo.zeroconfig.edge; + +import java.util.concurrent.CountDownLatch; +import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component("SelfServiceInvoker") +public class SelfServiceInvoker implements BootListener { + interface IServerEndpoint { + String getName(String name); + } + + @RpcReference(microserviceName = "demo-zeroconfig-registry-edge", schemaId = "ServerEndpoint") + IServerEndpoint endpoint; + + public CountDownLatch latch = new CountDownLatch(1); + + public String result = ""; + + public void onAfterRegistry(BootEvent event) { + result = endpoint.getName("hello"); + latch.countDown(); + } +} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/ServerEndpoint.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/ServerEndpoint.java new file mode 100644 index 00000000000..71f259366ae --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/ServerEndpoint.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.zeroconfig.edge; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import jakarta.ws.rs.core.MediaType; + +@RestSchema(schemaId = "ServerEndpoint") +@RequestMapping(path = "/edge/prefix", produces = MediaType.APPLICATION_JSON) +public class ServerEndpoint { + @GetMapping(path = "/getName") + public String getName(@RequestParam(name = "name") String name) { + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); + return name; + } +} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/resources/application.yml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/resources/application.yml new file mode 100644 index 00000000000..14cda29c28d --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- + +server: + port: 8888 + +servicecomb: + service: + application: demo-zeroconfig-registry + name: demo-zeroconfig-registry-edge + version: 0.0.2 + rest: + address: 0.0.0.0:8888 + filter-chains: + enabled: true + + http: + dispatcher: + edge: + default: + enabled: false + url: + enabled: true + pattern: /(.*) + mappings: + client: + prefixSegmentCount: 0 + path: "/register/url/prefix/.*" + microserviceName: demo-zeroconfig-registry-client + versionRule: 0+ + references: + transport: + demo-zeroconfig-registry-client.ClientServerEndpoint.contextMapper: rest + + context: + headerContextMapper: | + gatewayHeader: context-gateway-header + queryContextMapper: | + gatewayQuery: context-gateway-query diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/resources/log4j2.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/resources/log4j2.xml similarity index 100% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/resources/log4j2.xml rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/resources/log4j2.xml diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/pom.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/pom.xml new file mode 100644 index 00000000000..f69f1118a0a --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/pom.xml @@ -0,0 +1,118 @@ + + + + + 4.0.0 + demo-zeroconfig-registry-server + Java Chassis::Demo::ZeroConfig Registry Server + + org.apache.servicecomb.demo + demo-zeroconfig-registry + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.zeroconfig.server.ServerApplication + + + + jakarta.ws.rs + jakarta.ws.rs-api + + + org.apache.servicecomb + java-chassis-spring-boot-starter-servlet + + + org.apache.servicecomb + registry-zero-config + + + org.apache.servicecomb.demo + demo-schema + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + org.commonjava.maven.plugins + directory-maven-plugin + + + + + + diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceEndpoint.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceEndpoint.java new file mode 100644 index 00000000000..454e3cbba36 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceEndpoint.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.zeroconfig.server; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.ws.rs.core.Response.Status; + +@RestSchema(schemaId = "GovernanceEndpoint") +@RequestMapping("/governance") +public class GovernanceEndpoint { + private static final Logger LOGGER = LoggerFactory.getLogger(GovernanceEndpoint.class); + + private Map retryTimes = new HashMap<>(); + + @GetMapping("/hello") + public String sayHello() { + return "Hello world!"; + } + + @GetMapping("/retry") + @ApiResponses({ + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)), description = ""), + @ApiResponse(responseCode = "502", content = @Content(schema = @Schema(implementation = String.class)), description = "")}) + public String retry(@RequestParam(name = "invocationID") String invocationID) { + LOGGER.info("invoke service: {}", invocationID); + retryTimes.putIfAbsent(invocationID, 0); + retryTimes.put(invocationID, retryTimes.get(invocationID) + 1); + + int retry = retryTimes.get(invocationID); + + if (retry == 3) { + return "try times: " + retry; + } + throw new InvocationException(502, "retry", "retry"); + } + + @GetMapping("/retryRpc") + @ApiResponses({ + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)), description = ""), + @ApiResponse(responseCode = "502", content = @Content(schema = @Schema(implementation = String.class)), description = "")}) + public String retryRpc(@RequestParam(name = "invocationID") String invocationID) { + return retry(invocationID); + } + + @GetMapping("/circuitBreaker") + public String circuitBreaker() { + throw new InvocationException(Status.SERVICE_UNAVAILABLE, ""); + } +} diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceNoPrefixEndpoint.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceNoPrefixEndpoint.java similarity index 80% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceNoPrefixEndpoint.java rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceNoPrefixEndpoint.java index 4f08860c0c4..0c5121cac33 100644 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceNoPrefixEndpoint.java +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceNoPrefixEndpoint.java @@ -28,8 +28,10 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; @RestSchema(schemaId = "GovernanceNoPrefixEndpoint") @RequestMapping("/") @@ -40,8 +42,8 @@ public class GovernanceNoPrefixEndpoint { @GetMapping("/noPrefixRetry") @ApiResponses({ - @ApiResponse(code = 200, response = String.class, message = ""), - @ApiResponse(code = 502, response = String.class, message = "")}) + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)), description = ""), + @ApiResponse(responseCode = "502", content = @Content(schema = @Schema(implementation = String.class)), description = "")}) public String noPrefixRetry(@RequestParam(name = "invocationID") String invocationID) { LOGGER.info("invoke service: {}", invocationID); retryTimes.putIfAbsent(invocationID, 0); diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/RpcEndpoint.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/RpcEndpoint.java similarity index 100% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/RpcEndpoint.java rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/RpcEndpoint.java diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/SelfServiceInvoker.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/SelfServiceInvoker.java new file mode 100644 index 00000000000..1f8c6c800c8 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/SelfServiceInvoker.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.servicecomb.demo.zeroconfig.server; + +import java.util.concurrent.CountDownLatch; +import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component("SelfServiceInvoker") +public class SelfServiceInvoker implements BootListener { + interface IServerEndpoint { + String getName(String name); + } + + @RpcReference(microserviceName = "demo-zeroconfig-registry-server", schemaId = "ServerEndpoint") + IServerEndpoint endpoint; + + public CountDownLatch latch = new CountDownLatch(1); + + public String result = ""; + + public void onAfterRegistry(BootEvent event) { + result = endpoint.getName("hello"); + latch.countDown(); + } +} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerApplication.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerApplication.java new file mode 100644 index 00000000000..a04249fd63a --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerApplication.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.servicecomb.demo.zeroconfig.server; + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ServerApplication { + public static void main(final String[] args) throws Exception { + new SpringApplicationBuilder().sources(ServerApplication.class).web(WebApplicationType.SERVLET).build().run(args); + + SelfServiceInvoker invoker = BeanUtils.getBean("SelfServiceInvoker"); + invoker.latch.await(10, TimeUnit.SECONDS); + TestMgr.check(invoker.result, "hello"); + + TestMgr.summary(); + if (!TestMgr.errors().isEmpty()) { + System.exit(1); + } + } +} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerEndpoint.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerEndpoint.java new file mode 100644 index 00000000000..b0dd4ef5d97 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerEndpoint.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.zeroconfig.server; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import jakarta.ws.rs.core.MediaType; + +@RestSchema(schemaId = "ServerEndpoint") +@RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) +public class ServerEndpoint { + @GetMapping(path = "/getName") + public String getName(@RequestParam(name = "name") String name) { + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); + return name; + } +} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/resources/application.yml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/resources/application.yml new file mode 100644 index 00000000000..52863ee0bd7 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- + +server: + port: 8080 + +servicecomb: + service: + application: demo-zeroconfig-registry + name: demo-zeroconfig-registry-server + version: 0.0.2 + + rest: + address: 0.0.0.0:8080 + filter-chains: + enabled: true diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/resources/log4j2.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/resources/log4j2.xml similarity index 100% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/resources/log4j2.xml rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/resources/log4j2.xml diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/pom.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/pom.xml new file mode 100644 index 00000000000..8c57041ea1d --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/pom.xml @@ -0,0 +1,158 @@ + + + + + 4.0.0 + demo-zeroconfig-registry-tests + Java Chassis::Demo::ZeroConfig Registry Tests + + org.apache.servicecomb.demo + demo-zeroconfig-registry + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.zeroconfig.tests.Application + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-servlet + + + org.apache.servicecomb + registry-local + + + org.apache.servicecomb.demo + demo-schema + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + demo-zeroconfig-registry-server:${project.version} + demo-zeroconfig-registry-server + + + /maven/maven/demo-zeroconfig-registry-server-${project.version}.jar + + + + Service information is shown below + + + 8080 + + + + + + 8080:8080 + + + + + + demo-zeroconfig-registry-client:${project.version} + demo-zeroconfig-registry-client + + + /maven/maven/demo-zeroconfig-registry-client-${project.version}.jar + + + + Service information is shown below + + + 8082 + + + + + + 8082:8082 + + + + + + demo-zeroconfig-registry-edge:${project.version} + demo-zeroconfig-registry-edge + + + /maven/maven/demo-zeroconfig-registry-edge-${project.version}.jar + + + + Service information is shown below + + + 8888 + + + + + + 8888:8888 + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/Application.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/Application.java new file mode 100644 index 00000000000..3b98119d918 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/Application.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.zeroconfig.tests; + +import org.apache.servicecomb.demo.CategorizedTestCaseRunner; +import org.apache.servicecomb.demo.TestMgr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class Application { + private static final Logger LOGGER = LoggerFactory.getLogger(Application.class); + + public static void main(final String[] args) throws Exception { + new SpringApplicationBuilder().sources(Application.class).web(WebApplicationType.NONE) + .build().run(args); + + runTest(); + + TestMgr.summary(); + + LOGGER.info("-------------- last time updated checks(maybe more/less): 662 -------------"); + + if (!TestMgr.errors().isEmpty()) { + throw new IllegalStateException("tests failed"); + } + } + + private static void runTest() throws Exception { + CategorizedTestCaseRunner + .runCategorizedTestCase("demo-zeroconfig-registry-server"); + } +} diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ClientModel.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ClientModel.java similarity index 100% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ClientModel.java rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ClientModel.java diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/GovernanceTest.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/GovernanceTest.java similarity index 96% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/GovernanceTest.java rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/GovernanceTest.java index 83fd05f86c3..24899c822c6 100644 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/GovernanceTest.java +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/GovernanceTest.java @@ -26,13 +26,13 @@ import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; @Component public class GovernanceTest implements CategorizedTestCase { - String url = "servicecomb://demo-zeroconfig-schemadiscovery-registry-client/governance"; + String url = "servicecomb://demo-zeroconfig-registry-client/governance"; - RestTemplate template = RestTemplateBuilder.create(); + RestOperations template = RestTemplateBuilder.create(); @Override public void testRestTransport() throws Exception { diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ServerTest.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ServerTest.java similarity index 80% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ServerTest.java rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ServerTest.java index 363496a05ae..767f43ff74b 100644 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ServerTest.java +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ServerTest.java @@ -20,27 +20,28 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.Date; -import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CountDownLatch; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestOperations; import io.vertx.core.json.JsonObject; @Component public class ServerTest implements CategorizedTestCase { - RestTemplate template = RestTemplateBuilder.create(); + RestOperations template = RestTemplateBuilder.create(); @Override public void testRestTransport() throws Exception { @@ -57,7 +58,7 @@ private void testContextMapper() throws URISyntaxException { headers.add("clientHeader", "v1"); headers.add("gatewayHeader", "v2"); RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, - new URI("cse://demo-zeroconfig-schemadiscovery-registry-edge/register/url/prefix/contextMapper?clientQuery=v3&" + new URI("cse://demo-zeroconfig-registry-edge/register/url/prefix/contextMapper?clientQuery=v3&" + "gatewayQuery=v4")); // test two times to check different transport(only use rest) ResponseEntity response = template.exchange(requestEntity, String.class); @@ -74,7 +75,7 @@ private void testDateForEdge() { clientModelReq.setUpdateDate(date); Map response = template .postForObject( - "cse://demo-zeroconfig-schemadiscovery-registry-edge" + "cse://demo-zeroconfig-registry-edge" + "/register/url/prefix/postModel", clientModelReq, Map.class); Object result = response.get("updateDate"); @@ -89,12 +90,12 @@ private void testDateForEdge() { } private void testServerGetName() throws Exception { - // invoke demo-zeroconfig-schemadiscovery-registry-client + // invoke demo-zeroconfig-registry-client TestMgr.check("world", template .getForObject( - "cse://demo-zeroconfig-schemadiscovery-registry-client/register/url/prefix/getName?name=world", + "cse://demo-zeroconfig-registry-client/register/url/prefix/getName?name=world", String.class)); - // invoke demo-zeroconfig-schemadiscovery-registry-edge + // invoke demo-zeroconfig-registry-edge // create many threads to test event-loop not blocking int thread = 32; CountDownLatch latch = new CountDownLatch(thread); @@ -104,7 +105,7 @@ private void testServerGetName() throws Exception { try { TestMgr.check("world", template .getForObject( - "cse://demo-zeroconfig-schemadiscovery-registry-edge/register/url/prefix/getName?name=world", + "cse://demo-zeroconfig-registry-edge/register/url/prefix/getName?name=world", String.class)); } catch (Exception e) { TestMgr.failed("test failed", e); @@ -119,18 +120,22 @@ private void testServerGetName() throws Exception { @SuppressWarnings("rawTypes") private void testGetAllMicroservice() { - // invoke demo-zeroconfig-schemadiscovery-registry-client + // invoke demo-zeroconfig-registry-client TestMgr.check("2", template - .getForObject( - "cse://demo-zeroconfig-schemadiscovery-registry-client" - + "/register/url/prefix/getRegisteredMicroservice", - List.class).size()); - // invoke demo-zeroconfig-schemadiscovery-registry-edge + .exchange( + "cse://demo-zeroconfig-registry-client" + + "/register/url/prefix/getRegisteredMicroservice", HttpMethod.GET, null, + new ParameterizedTypeReference>() { + + }).getBody().size()); + // invoke demo-zeroconfig-registry-edge TestMgr.check("2", template - .getForObject( - "cse://demo-zeroconfig-schemadiscovery-registry-edge" - + "/register/url/prefix/getRegisteredMicroservice", - List.class).size()); + .exchange( + "cse://demo-zeroconfig-registry-edge" + + "/register/url/prefix/getRegisteredMicroservice", HttpMethod.GET, null, + new ParameterizedTypeReference>() { + + }).getBody().size()); } private void testJsonObject() { @@ -142,7 +147,7 @@ private void testJsonObject() { JsonObject result = template .postForObject( - "cse://demo-zeroconfig-schemadiscovery-registry-client" + "cse://demo-zeroconfig-registry-client" + "/register/url/prefix/jsonObject", in, JsonObject.class); TestMgr.check(inner.toString(), result.toString()); TestMgr.check(result.getString("hello"), "world"); @@ -152,7 +157,7 @@ private void testString() { String in = "{\"hello\":\"world\"}"; String result = template .postForObject( - "cse://demo-zeroconfig-schemadiscovery-registry-client" + "cse://demo-zeroconfig-registry-client" + "/register/url/prefix/getString", in, String.class); TestMgr.check(in, result); } diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/application.yml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/application.yml new file mode 100644 index 00000000000..f9f987e88ef --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: demo-zeroconfig-registry + name: demo-zeroconfig-registry-tests + version: 0.0.2 + handler: + chain: + Consumer: + default: loadbalance + loadbalance: + filter: + isolation: + enabled: false diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/resources/logback.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/logback.xml similarity index 100% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/resources/logback.xml rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/logback.xml diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-client/ClientServerEndpoint.yaml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-client/ClientServerEndpoint.yaml new file mode 100644 index 00000000000..d8e19afb43c --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-client/ClientServerEndpoint.yaml @@ -0,0 +1,155 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.demo.zeroconfig.client.ClientServerEndpoint + version: 1.0.0 +servers: +- url: /register/url/prefix +paths: + /contextMapper: + get: + operationId: contextMapper + parameters: + - name: gatewayHeader + in: header + required: true + schema: + type: string + - name: clientHeader + in: header + required: true + schema: + type: string + - name: gatewayQuery + in: query + required: true + schema: + type: string + - name: clientQuery + in: query + required: true + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /getName: + get: + operationId: getName + parameters: + - name: name + in: query + required: true + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /getRegisteredMicroservice: + get: + operationId: getRegisteredMicroservice + responses: + "200": + description: response of 200 + content: + application/json: + schema: + uniqueItems: true + type: array + items: + type: string + /getString: + post: + operationId: getString + requestBody: + content: + application/json: + schema: + type: string + required: true + x-raw-json: true + x-name: jsonString + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /jsonObject: + post: + operationId: jsonObject + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/JsonObject' + required: true + x-name: jsonObject + responses: + "200": + description: response of 200 + content: + application/json: + schema: + $ref: '#/components/schemas/JsonObject' + /postModel: + post: + operationId: postModel + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ClientModel' + required: true + x-name: clientModel + responses: + "200": + description: response of 200 + content: + application/json: + schema: + $ref: '#/components/schemas/ClientModel' +components: + schemas: + JsonObject: + type: object + properties: + map: + type: object + additionalProperties: + type: object + empty: + type: boolean + x-java-class: io.vertx.core.json.JsonObject + ClientModel: + type: object + properties: + updateDate: + type: string + format: date-time + x-java-class: org.apache.servicecomb.demo.zeroconfig.client.ClientModel diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-client/GovernanceEndpoint.yaml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-client/GovernanceEndpoint.yaml new file mode 100644 index 00000000000..6dfbd0d36c8 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-client/GovernanceEndpoint.yaml @@ -0,0 +1,114 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.demo.zeroconfig.client.GovernanceEndpoint + version: 1.0.0 +servers: +- url: /governance +paths: + /bulkhead: + get: + operationId: bulkhead + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /circuitBreaker: + get: + operationId: circuitBreaker + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /hello: + get: + operationId: hello + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /helloRpc: + get: + operationId: helloRpc + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /noPrefixRetry: + get: + operationId: noPrefixRetry + parameters: + - name: invocationID + in: query + required: true + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /retry: + get: + operationId: retry + parameters: + - name: invocationID + in: query + required: true + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /retryRpc: + get: + operationId: retryRpc + parameters: + - name: invocationID + in: query + required: true + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string +components: + schemas: {} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-client/SchemaDiscoveryEndpoint.yaml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-client/SchemaDiscoveryEndpoint.yaml new file mode 100644 index 00000000000..8a34262bfbe --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-client/SchemaDiscoveryEndpoint.yaml @@ -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. +## --------------------------------------------------------------------------- + +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.schemadiscovery.SchemaDiscoveryEndpoint + version: 1.0.0 +servers: +- url: /v1/schema/discovery +paths: + /getSchema: + get: + operationId: getSchema + parameters: + - name: schemaId + in: query + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string +components: + schemas: {} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-edge/ClientServerEndpoint.yaml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-edge/ClientServerEndpoint.yaml new file mode 100644 index 00000000000..d8e19afb43c --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-edge/ClientServerEndpoint.yaml @@ -0,0 +1,155 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.demo.zeroconfig.client.ClientServerEndpoint + version: 1.0.0 +servers: +- url: /register/url/prefix +paths: + /contextMapper: + get: + operationId: contextMapper + parameters: + - name: gatewayHeader + in: header + required: true + schema: + type: string + - name: clientHeader + in: header + required: true + schema: + type: string + - name: gatewayQuery + in: query + required: true + schema: + type: string + - name: clientQuery + in: query + required: true + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /getName: + get: + operationId: getName + parameters: + - name: name + in: query + required: true + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /getRegisteredMicroservice: + get: + operationId: getRegisteredMicroservice + responses: + "200": + description: response of 200 + content: + application/json: + schema: + uniqueItems: true + type: array + items: + type: string + /getString: + post: + operationId: getString + requestBody: + content: + application/json: + schema: + type: string + required: true + x-raw-json: true + x-name: jsonString + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /jsonObject: + post: + operationId: jsonObject + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/JsonObject' + required: true + x-name: jsonObject + responses: + "200": + description: response of 200 + content: + application/json: + schema: + $ref: '#/components/schemas/JsonObject' + /postModel: + post: + operationId: postModel + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ClientModel' + required: true + x-name: clientModel + responses: + "200": + description: response of 200 + content: + application/json: + schema: + $ref: '#/components/schemas/ClientModel' +components: + schemas: + JsonObject: + type: object + properties: + map: + type: object + additionalProperties: + type: object + empty: + type: boolean + x-java-class: io.vertx.core.json.JsonObject + ClientModel: + type: object + properties: + updateDate: + type: string + format: date-time + x-java-class: org.apache.servicecomb.demo.zeroconfig.client.ClientModel diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-edge/SchemaDiscoveryEndpoint.yaml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-edge/SchemaDiscoveryEndpoint.yaml new file mode 100644 index 00000000000..8a34262bfbe --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-edge/SchemaDiscoveryEndpoint.yaml @@ -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. +## --------------------------------------------------------------------------- + +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.schemadiscovery.SchemaDiscoveryEndpoint + version: 1.0.0 +servers: +- url: /v1/schema/discovery +paths: + /getSchema: + get: + operationId: getSchema + parameters: + - name: schemaId + in: query + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string +components: + schemas: {} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/registry.yaml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/registry.yaml new file mode 100644 index 00000000000..07360eabfde --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/registry.yaml @@ -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. +## --------------------------------------------------------------------------- + +demo-zeroconfig-registry-client: + - id: "001" + version: "0.0.2" + appid: demo-zeroconfig-registry + schemaIds: + - ClientServerEndpoint + - SchemaDiscoveryEndpoint + - GovernanceEndpoint + instances: + - endpoints: + - rest://localhost:8082 +demo-zeroconfig-registry-edge: + - id: "002" + version: "0.0.2" + appid: demo-zeroconfig-registry + schemaIds: + - ClientServerEndpoint + - SchemaDiscoveryEndpoint + instances: + - endpoints: + - rest://localhost:8888 diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/test/java/org/apache/servicecomb/demo/zeroconfig/tests/ZeroConfigRegistryIT.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/test/java/org/apache/servicecomb/demo/zeroconfig/tests/ZeroConfigRegistryIT.java similarity index 100% rename from demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/test/java/org/apache/servicecomb/demo/zeroconfig/tests/ZeroConfigRegistryIT.java rename to demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/test/java/org/apache/servicecomb/demo/zeroconfig/tests/ZeroConfigRegistryIT.java diff --git a/demo/demo-zeroconfig-registry/pom.xml b/demo/demo-zeroconfig-registry/pom.xml new file mode 100644 index 00000000000..bc02525e483 --- /dev/null +++ b/demo/demo-zeroconfig-registry/pom.xml @@ -0,0 +1,60 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-zeroconfig-registry + Java Chassis::Demo::ZeroConfig Registry + pom + + demo-zeroconfig-registry-server + demo-zeroconfig-registry-edge + demo-zeroconfig-registry-client + demo-zeroconfig-registry-tests + + + + + org.apache.servicecomb + solution-basic + + + org.apache.servicecomb + foundation-test-scaffolding + compile + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/README.md b/demo/demo-zeroconfig-schemadiscovery-registry/README.md deleted file mode 100644 index 44cd5539ce9..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Introduction - -This demo tests zeroconfig. - -* server - - run in docker - -* client - - run in docker, and find server instances through zeroconfig, find schemas through - schema discovery - -* edge - run in docker, and find server instances through zeroconfig, find schemas through - schema discovery - -* tests - - integration tests run in host machine, find client/edge instances through local, - and schemas from schema discovery. \ No newline at end of file diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/pom.xml b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/pom.xml deleted file mode 100644 index 096ba0fd49d..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/pom.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - 4.0.0 - demo-zeroconfig-schemadiscovery-registry-client - Java Chassis::Demo::ZeroConfig-SchemaDiscovery Registry Client - - org.apache.servicecomb.demo - demo-zeroconfig-schemadiscovery-registry - 3.0.0-SNAPSHOT - - - - org.apache.servicecomb.demo.zeroconfig.client.ClientApplication - - - - jakarta.ws.rs - jakarta.ws.rs-api - - - org.apache.servicecomb - java-chassis-spring-boot-starter-servlet - - - org.apache.servicecomb - handler-governance - - - org.apache.servicecomb - registry-schema-discovery - - - org.apache.servicecomb - registry-zero-config - - - org.apache.servicecomb.demo - demo-schema - - - org.apache.servicecomb - config-cc - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - - docker - - - - io.fabric8 - docker-maven-plugin - - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - - - - - - diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientServerEndpoint.java b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientServerEndpoint.java deleted file mode 100644 index 9355b3b961d..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientServerEndpoint.java +++ /dev/null @@ -1,104 +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.servicecomb.demo.zeroconfig.client; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.ws.rs.core.MediaType; - -import org.apache.servicecomb.provider.pojo.RpcReference; -import org.apache.servicecomb.provider.rest.common.RestSchema; -import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; -import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.apache.servicecomb.swagger.invocation.context.InvocationContext; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -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.RequestParam; - -import io.vertx.core.json.JsonObject; - -@RestSchema(schemaId = "ClientServerEndpoint") -@RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) -public class ClientServerEndpoint { - @RpcReference(microserviceName = "demo-zeroconfig-schemadiscovery-registry-server", schemaId = "ServerEndpoint") - private IServerEndpoint serverEndpoint; - - @RpcReference(microserviceName = "demo-zeroconfig-schemadiscovery-registry-server", schemaId = "RpcEndpoint") - private IRpcEndpoint rpcEndpoint; - - @GetMapping(path = "/getName") - public String getName(@RequestParam(name = "name") String name) { - return serverEndpoint.getName(name); - } - - @GetMapping(path = "/getRegisteredMicroservice") - public Set getRegisteredMicroservice() { - List microserviceList = DiscoveryManager.INSTANCE.getAllMicroservices(); - Set names = new HashSet<>(); - - for (Microservice m : microserviceList) { - if (m.getServiceName().equals("demo-zeroconfig-schemadiscovery-registry-client") - || m.getServiceName().equals("demo-zeroconfig-schemadiscovery-registry-server")) { - names.add(m.getServiceName()); - } - } - return names; - } - - @PostMapping(path = "/jsonObject") - public JsonObject jsonObject(@RequestBody JsonObject jsonObject) { - JsonObject param = new JsonObject(); - param.put("map", jsonObject); - JsonObject message = rpcEndpoint.getJsonObject(param); - JsonObject inner = new JsonObject(); - inner.put("map", message); - return inner; - } - - @PostMapping(path = "/getString") - public String getString(@RawJsonRequestBody String jsonString) { - return jsonString; - } - - @PostMapping(path = "/postModel") - public ClientModel postModel(@RequestBody ClientModel clientModel) { - return clientModel; - } - - @GetMapping(path = "/contextMapper") - public String contextMapper(@RequestHeader("gatewayHeader") String gatewayHeader, - @RequestHeader("clientHeader") String clientHeader, - @RequestParam("gatewayQuery") String gatewayQuery, - @RequestParam("clientQuery") String clientQuery) { - InvocationContext context = ContextUtils.getInvocationContext(); - if (gatewayHeader.equals(context.getContext("context-gateway-header")) && - clientHeader.equals(context.getContext("context-client-header")) && - gatewayQuery.equals(context.getContext("context-gateway-query")) && - clientQuery.equals(context.getContext("context-client-query"))) { - return "success"; - } - return "fail"; - } -} diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/GovernanceEndpoint.java b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/GovernanceEndpoint.java deleted file mode 100644 index 9bb256d251a..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/GovernanceEndpoint.java +++ /dev/null @@ -1,83 +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.servicecomb.demo.zeroconfig.client; - -import javax.ws.rs.core.Response.Status; - -import org.apache.servicecomb.provider.rest.common.RestSchema; -import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.client.RestTemplate; - -@RestSchema(schemaId = "GovernanceEndpoint") -@RequestMapping("/governance") -public class GovernanceEndpoint { - private static final String SERVER = "servicecomb://demo-zeroconfig-schemadiscovery-registry-server"; - - private RestTemplate restTemplate = RestTemplateBuilder.create(); - - private int count = 0; - - @GetMapping("/hello") - public String hello() { - return restTemplate.getForObject(SERVER + "/governance/hello", String.class); - } - - @GetMapping("/helloRpc") - public String helloRpc() { - return restTemplate.getForObject(SERVER + "/governance/hello", String.class); - } - - @GetMapping("/noPrefixRetry") - public String noPrefixRetry(@RequestParam(name = "invocationID") String invocationID) { - return restTemplate - .getForObject(SERVER + "/noPrefixRetry?invocationID={1}", String.class, - invocationID); - } - - @GetMapping("/retry") - public String retry(@RequestParam(name = "invocationID") String invocationID) { - return restTemplate - .getForObject(SERVER + "/governance/retry?invocationID={1}", String.class, - invocationID); - } - - @GetMapping("/retryRpc") - public String retryRpc(@RequestParam(name = "invocationID") String invocationID) { - return restTemplate - .getForObject(SERVER + "/governance/retryRpc?invocationID={1}", String.class, - invocationID); - } - - @GetMapping("/circuitBreaker") - public String circuitBreaker() throws Exception { - count++; - if (count % 3 == 0) { - return "ok"; - } - throw new InvocationException(Status.SERVICE_UNAVAILABLE, "test error"); - } - - @GetMapping("/bulkhead") - public String bulkhead() { - return restTemplate.getForObject(SERVER + "/governance/hello", String.class); - } -} diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/resources/application.yml b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/resources/application.yml deleted file mode 100644 index b633b98b514..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/resources/application.yml +++ /dev/null @@ -1,111 +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. -## --------------------------------------------------------------------------- - -server: - port: 8082 - -# java-chassis configurations - -APPLICATION_ID: demo-zeroconfig-schemadiscovery-registry -service_description: - name: demo-zeroconfig-schemadiscovery-registry-client - version: 0.0.2 -servicecomb: - rest: - address: 0.0.0.0:8082 - highway: - address: 0.0.0.0:8084 - config: - client: - # for testing dynamic configuration - # serverUri: http://127.0.0.1:30113 - refreshMode: 0 - # refresh_interval: 5000 - # refreshPort: 30114 - governance: - GovernanceEndpoint.helloRpc: - matchType: rpc - demo-zeroconfig-schemadiscovery-registry-server.GovernanceEndpoint.retryRpc: - matchType: rpc - matchGroup: - demo-rateLimiting: | - matches: - - apiPath: - exact: "/governance/hello" - demo-rateLimiting-rpc: | - matches: - - apiPath: - exact: "GovernanceEndpoint.helloRpc" - demo-retry: | - matches: - - apiPath: - exact: "/governance/retry" - demo-retry-no-prefex: | - matches: - - apiPath: - exact: "/noPrefixRetry" - demo-retry-rpc: | - matches: - - apiPath: - exact: "demo-zeroconfig-schemadiscovery-registry-server.GovernanceEndpoint.retryRpc" - demo-circuitBreaker: | - matches: - - apiPath: - exact: "/governance/circuitBreaker" - demo-bulkhead: | - matches: - - apiPath: - exact: "/governance/bulkhead" - rateLimiting: - demo-rateLimiting: | - rate: 10 - ## services is optional in configuration file - services: demo-zeroconfig-schemadiscovery-registry-client - demo-rateLimiting-rpc: | - rate: 10 - ## services is optional in configuration file - services: demo-zeroconfig-schemadiscovery-registry-client - retry: - demo-retry: | - maxAttempts: 3 - ## services is optional in configuration file - services: demo-zeroconfig-schemadiscovery-registry-client - demo-retry-no-prefex: | - maxAttempts: 3 - ## services is optional in configuration file - services: demo-zeroconfig-schemadiscovery-registry-client - demo-retry-rpc: | - maxAttempts: 3 - ## services is optional in configuration file - services: demo-zeroconfig-schemadiscovery-registry-client - circuitBreaker: - demo-circuitBreaker: | - minimumNumberOfCalls: 10 - slidingWindowSize: 10 - failureRateThreshold: 20 - ## services is optional in configuration file - services: demo-zeroconfig-schemadiscovery-registry-client - bulkhead: - demo-bulkhead: | - maxConcurrentCalls: 5 - ## services is optional in configuration file - services: demo-zeroconfig-schemadiscovery-registry-client - context: - headerContextMapper: | - clientHeader: context-client-header - queryContextMapper: | - clientQuery: context-client-query diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/pom.xml b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/pom.xml deleted file mode 100644 index a0572b854eb..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/pom.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - 4.0.0 - demo-zeroconfig-schemadiscovery-registry-edge - Java Chassis::Demo::ZeroConfig-SchemaDiscovery Registry Edge - - org.apache.servicecomb.demo - demo-zeroconfig-schemadiscovery-registry - 3.0.0-SNAPSHOT - - - - org.apache.servicecomb.demo.zeroconfig.edge.EdgeApplication - - - - jakarta.ws.rs - jakarta.ws.rs-api - - - org.apache.servicecomb - edge-core - - - org.apache.servicecomb - java-chassis-spring-boot-starter-standalone - - - org.apache.servicecomb - registry-schema-discovery - - - org.apache.servicecomb - registry-zero-config - - - org.apache.servicecomb.demo - demo-schema - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - - docker - - - - io.fabric8 - docker-maven-plugin - - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - - - - - - \ No newline at end of file diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/SelfServiceInvoker.java b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/SelfServiceInvoker.java deleted file mode 100644 index e6535240ac3..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/SelfServiceInvoker.java +++ /dev/null @@ -1,42 +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.servicecomb.demo.zeroconfig.edge; - -import java.util.concurrent.CountDownLatch; -import org.apache.servicecomb.core.BootListener; -import org.apache.servicecomb.provider.pojo.RpcReference; -import org.springframework.stereotype.Component; - -@Component("SelfServiceInvoker") -public class SelfServiceInvoker implements BootListener { - interface IServerEndpoint { - String getName(String name); - } - - @RpcReference(microserviceName = "demo-zeroconfig-schemadiscovery-registry-edge", schemaId = "ServerEndpoint") - IServerEndpoint endpoint; - - public CountDownLatch latch = new CountDownLatch(1); - - public String result = ""; - - public void onAfterRegistry(BootEvent event) { - result = endpoint.getName("hello"); - latch.countDown(); - } -} diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/ServerEndpoint.java b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/ServerEndpoint.java deleted file mode 100644 index 24e25ed3ae3..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/ServerEndpoint.java +++ /dev/null @@ -1,41 +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.servicecomb.demo.zeroconfig.edge; - -import javax.ws.rs.core.MediaType; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.provider.rest.common.RestSchema; -import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; - -@RestSchema(schemaId = "ServerEndpoint") -@RequestMapping(path = "/edge/prefix", produces = MediaType.APPLICATION_JSON) -public class ServerEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(ServerEndpoint.class); - - @GetMapping(path = "/getName") - public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); - return name; - } -} diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/resources/application.yml b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/resources/application.yml deleted file mode 100644 index 561df5eb50e..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/resources/application.yml +++ /dev/null @@ -1,55 +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. -## --------------------------------------------------------------------------- - -server: - port: 8888 - -# java-chassis configurations - -APPLICATION_ID: demo-zeroconfig-schemadiscovery-registry -service_description: - name: demo-zeroconfig-schemadiscovery-registry-edge - version: 0.0.2 -servicecomb: - rest: - address: 0.0.0.0:8888 - filter-chains: - enabled: true - - http: - dispatcher: - edge: - default: - enabled: false - url: - enabled: true - pattern: /(.*) - mappings: - client: - prefixSegmentCount: 0 - path: "/register/url/prefix/.*" - microserviceName: demo-zeroconfig-schemadiscovery-registry-client - versionRule: 0+ - references: - transport: - demo-zeroconfig-schemadiscovery-registry-client.ClientServerEndpoint.contextMapper: rest - - context: - headerContextMapper: | - gatewayHeader: context-gateway-header - queryContextMapper: | - gatewayQuery: context-gateway-query diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/pom.xml b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/pom.xml deleted file mode 100644 index 95944af2e4b..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/pom.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - 4.0.0 - demo-zeroconfig-schemadiscovery-registry-server - Java Chassis::Demo::ZeroConfig-SchemaDiscovery Registry Server - - org.apache.servicecomb.demo - demo-zeroconfig-schemadiscovery-registry - 3.0.0-SNAPSHOT - - - - org.apache.servicecomb.demo.zeroconfig.server.ServerApplication - - - - jakarta.ws.rs - jakarta.ws.rs-api - - - org.apache.servicecomb - java-chassis-spring-boot-starter-servlet - - - org.apache.servicecomb - handler-governance - - - org.apache.servicecomb - registry-schema-discovery - - - org.apache.servicecomb - registry-zero-config - - - org.apache.servicecomb.demo - demo-schema - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - - docker - - - - io.fabric8 - docker-maven-plugin - - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - - - - - - \ No newline at end of file diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceEndpoint.java b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceEndpoint.java deleted file mode 100644 index 2ae69df0dd8..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceEndpoint.java +++ /dev/null @@ -1,77 +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.servicecomb.demo.zeroconfig.server; - -import java.util.HashMap; -import java.util.Map; - -import javax.ws.rs.core.Response.Status; - -import org.apache.servicecomb.provider.rest.common.RestSchema; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; - -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; - -@RestSchema(schemaId = "GovernanceEndpoint") -@RequestMapping("/governance") -public class GovernanceEndpoint { - private static final Logger LOGGER = LoggerFactory.getLogger(GovernanceEndpoint.class); - - private Map retryTimes = new HashMap<>(); - - @GetMapping("/hello") - public String sayHello() { - return "Hello world!"; - } - - @GetMapping("/retry") - @ApiResponses({ - @ApiResponse(code = 200, response = String.class, message = ""), - @ApiResponse(code = 502, response = String.class, message = "")}) - public String retry(@RequestParam(name = "invocationID") String invocationID) { - LOGGER.info("invoke service: {}", invocationID); - retryTimes.putIfAbsent(invocationID, 0); - retryTimes.put(invocationID, retryTimes.get(invocationID) + 1); - - int retry = retryTimes.get(invocationID); - - if (retry == 3) { - return "try times: " + retry; - } - throw new InvocationException(502, "retry", "retry"); - } - - @GetMapping("/retryRpc") - @ApiResponses({ - @ApiResponse(code = 200, response = String.class, message = ""), - @ApiResponse(code = 502, response = String.class, message = "")}) - public String retryRpc(@RequestParam(name = "invocationID") String invocationID) { - return retry(invocationID); - } - - @GetMapping("/circuitBreaker") - public String circuitBreaker() { - throw new InvocationException(Status.SERVICE_UNAVAILABLE, ""); - } -} diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/SelfServiceInvoker.java b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/SelfServiceInvoker.java deleted file mode 100644 index 0a52776fc8e..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/SelfServiceInvoker.java +++ /dev/null @@ -1,42 +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.servicecomb.demo.zeroconfig.server; - -import java.util.concurrent.CountDownLatch; -import org.apache.servicecomb.core.BootListener; -import org.apache.servicecomb.provider.pojo.RpcReference; -import org.springframework.stereotype.Component; - -@Component("SelfServiceInvoker") -public class SelfServiceInvoker implements BootListener { - interface IServerEndpoint { - String getName(String name); - } - - @RpcReference(microserviceName = "demo-zeroconfig-schemadiscovery-registry-server", schemaId = "ServerEndpoint") - IServerEndpoint endpoint; - - public CountDownLatch latch = new CountDownLatch(1); - - public String result = ""; - - public void onAfterRegistry(BootEvent event) { - result = endpoint.getName("hello"); - latch.countDown(); - } -} diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerApplication.java b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerApplication.java deleted file mode 100644 index 271c2c83432..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerApplication.java +++ /dev/null @@ -1,43 +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.servicecomb.demo.zeroconfig.server; - -import java.util.concurrent.TimeUnit; -import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.builder.SpringApplicationBuilder; - -@SpringBootApplication -@EnableServiceComb -public class ServerApplication { - public static void main(final String[] args) throws Exception { - new SpringApplicationBuilder().sources(ServerApplication.class).web(WebApplicationType.SERVLET).build().run(args); - - SelfServiceInvoker invoker = BeanUtils.getBean("SelfServiceInvoker"); - invoker.latch.await(10, TimeUnit.SECONDS); - TestMgr.check(invoker.result, "hello"); - - TestMgr.summary(); - if (!TestMgr.errors().isEmpty()) { - System.exit(1); - } - } -} diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerEndpoint.java b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerEndpoint.java deleted file mode 100644 index d010caa1e28..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerEndpoint.java +++ /dev/null @@ -1,41 +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.servicecomb.demo.zeroconfig.server; - -import javax.ws.rs.core.MediaType; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.provider.rest.common.RestSchema; -import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; - -@RestSchema(schemaId = "ServerEndpoint") -@RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) -public class ServerEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(ServerEndpoint.class); - - @GetMapping(path = "/getName") - public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); - return name; - } -} diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/resources/application.yml b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/resources/application.yml deleted file mode 100644 index ea19b6fadea..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/resources/application.yml +++ /dev/null @@ -1,34 +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. -## --------------------------------------------------------------------------- - -server: - port: 8080 - -# java-chassis configurations - -APPLICATION_ID: demo-zeroconfig-schemadiscovery-registry -service_description: - name: demo-zeroconfig-schemadiscovery-registry-server - version: 0.0.2 -servicecomb: - service: - zero-config: - mode: multicast - rest: - address: 0.0.0.0:8080 - filter-chains: - enabled: true diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/pom.xml b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/pom.xml deleted file mode 100644 index d357644005c..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/pom.xml +++ /dev/null @@ -1,196 +0,0 @@ - - - - - 4.0.0 - demo-zeroconfig-schemadiscovery-registry-tests - Java Chassis::Demo::ZeroConfig-SchemaDiscovery Registry Tests - - org.apache.servicecomb.demo - demo-zeroconfig-schemadiscovery-registry - 3.0.0-SNAPSHOT - - - - org.apache.servicecomb.demo.zeroconfig.tests.Application - - - - org.apache.servicecomb - java-chassis-spring-boot-starter-servlet - - - org.apache.servicecomb - registry-schema-discovery - - - org.apache.servicecomb.demo - demo-schema - - - - - - docker - - - - - io.fabric8 - docker-maven-plugin - - - - demo-zeroconfig-schemadiscovery-registry-server:${project.version} - demo-zeroconfig-schemadiscovery-registry-server - - - /maven/maven/demo-zeroconfig-schemadiscovery-registry-server-${project.version}.jar - - - - Service information is shown below - - - 8080 - - - - - - 8080:8080 - - - - - - demo-zeroconfig-schemadiscovery-registry-client:${project.version} - demo-zeroconfig-schemadiscovery-registry-client - - - /maven/maven/demo-zeroconfig-schemadiscovery-registry-client-${project.version}.jar - - - - Service information is shown below - - - 8082 - - - - - - 8082:8082 - - - - - - demo-zeroconfig-schemadiscovery-registry-edge:${project.version} - demo-zeroconfig-schemadiscovery-registry-edge - - - /maven/maven/demo-zeroconfig-schemadiscovery-registry-edge-${project.version}.jar - - - - Service information is shown below - - - 8888 - - - - - - 8888:8888 - - - - - - - - start - pre-integration-test - - start - - - - stop - post-integration-test - - stop - - - - - - - - - io.fabric8 - docker-maven-plugin - - - org.codehaus.gmavenplus - gmavenplus-plugin - - - add-default-properties - initialize - - execute - - - - - - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - ${jacoco.failsafe.argLine} - - - - - integration-test - verify - - - - - - - - - - - diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/Application.java b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/Application.java deleted file mode 100644 index ce7a4decf09..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/Application.java +++ /dev/null @@ -1,47 +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.servicecomb.demo.zeroconfig.tests; - -import org.apache.servicecomb.demo.CategorizedTestCaseRunner; -import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.springboot.starter.EnableServiceComb; -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.builder.SpringApplicationBuilder; - -@SpringBootApplication -@EnableServiceComb -public class Application { - - public static void main(final String[] args) throws Exception { - new SpringApplicationBuilder().sources(Application.class).web(WebApplicationType.NONE) - .build().run(args); - - runTest(); - - TestMgr.summary(); - if (!TestMgr.errors().isEmpty()) { - throw new IllegalStateException("tests failed"); - } - } - - private static void runTest() throws Exception { - CategorizedTestCaseRunner - .runCategorizedTestCase("demo-zeroconfig-schemadiscovery-registry-server"); - } -} diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/resources/application.yml b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/resources/application.yml deleted file mode 100644 index 5144af71875..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/resources/application.yml +++ /dev/null @@ -1,32 +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. -## --------------------------------------------------------------------------- - -# java-chassis configurations - -APPLICATION_ID: demo-zeroconfig-schemadiscovery-registry -service_description: - name: demo-zeroconfig-schemadiscovery-registry-tests - version: 0.0.2 -servicecomb: - handler: - chain: - Consumer: - default: loadbalance - loadbalance: - filter: - isolation: - enabled: false diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/resources/microservices/demo-zeroconfig-schemadiscovery-registry-edge/ClientServerEndpoint.yaml b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/resources/microservices/demo-zeroconfig-schemadiscovery-registry-edge/ClientServerEndpoint.yaml deleted file mode 100644 index 7cf4edd1d68..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/resources/microservices/demo-zeroconfig-schemadiscovery-registry-edge/ClientServerEndpoint.yaml +++ /dev/null @@ -1,141 +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. -## --------------------------------------------------------------------------- - -swagger: "2.0" -info: - version: "1.0.0" - title: "swagger definition for org.apache.servicecomb.demo.zeroconfig.client.ClientServerEndpoint" - x-java-interface: "gen.swagger.ClientServerEndpointIntf" -basePath: "/register/url/prefix" -schemes: - - "http" -consumes: - - "application/json" -produces: - - "application/json" -paths: - /contextMapper: - get: - operationId: "contextMapper" - parameters: - - name: "gatewayHeader" - in: "header" - required: true - type: "string" - - name: "clientHeader" - in: "header" - required: true - type: "string" - - name: "gatewayQuery" - in: "query" - required: true - type: "string" - - name: "clientQuery" - in: "query" - required: true - type: "string" - responses: - "200": - description: "response of 200" - schema: - type: "string" - /getName: - get: - operationId: "getName" - parameters: - - name: "name" - in: "query" - required: true - type: "string" - responses: - "200": - description: "response of 200" - schema: - type: "string" - /getRegisteredMicroservice: - get: - operationId: "getRegisteredMicroservice" - parameters: [] - responses: - "200": - description: "response of 200" - schema: - type: "array" - items: - type: "string" - uniqueItems: true - /getString: - post: - operationId: "getString" - parameters: - - in: "body" - name: "jsonString" - required: true - schema: - type: "string" - x-raw-json: true - responses: - "200": - description: "response of 200" - schema: - type: "string" - /jsonObject: - post: - operationId: "jsonObject" - parameters: - - in: "body" - name: "jsonObject" - required: true - schema: - $ref: "#/definitions/JsonObject" - responses: - "200": - description: "response of 200" - schema: - $ref: "#/definitions/JsonObject" - /postModel: - post: - operationId: "postModel" - parameters: - - in: "body" - name: "clientModel" - required: true - schema: - $ref: "#/definitions/ClientModel" - responses: - "200": - description: "response of 200" - schema: - $ref: "#/definitions/ClientModel" -definitions: - JsonObject: - type: "object" - properties: - map: - type: "object" - additionalProperties: - type: "object" - empty: - type: "boolean" - x-java-class: "io.vertx.core.json.JsonObject" - ClientModel: - type: "object" - properties: - updateDate: - type: "string" - format: "date-time" - x-java-class: "org.apache.servicecomb.demo.zeroconfig.client.ClientModel" diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/resources/registry.yaml b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/resources/registry.yaml deleted file mode 100644 index cdd8fc782fa..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/resources/registry.yaml +++ /dev/null @@ -1,38 +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. -## --------------------------------------------------------------------------- - -demo-zeroconfig-schemadiscovery-registry-client: - - id: "001" - version: "0.0.2" - appid: demo-zeroconfig-schemadiscovery-registry - schemaIds: - - ClientServerEndpoint - - SchemaDiscoveryEndpoint - - GovernanceEndpoint - instances: - - endpoints: - - rest://localhost:8082 -demo-zeroconfig-schemadiscovery-registry-edge: - - id: "002" - version: "0.0.2" - appid: demo-zeroconfig-schemadiscovery-registry - schemaIds: - - ClientServerEndpoint - - SchemaDiscoveryEndpoint - instances: - - endpoints: - - rest://localhost:8888 \ No newline at end of file diff --git a/demo/demo-zeroconfig-schemadiscovery-registry/pom.xml b/demo/demo-zeroconfig-schemadiscovery-registry/pom.xml deleted file mode 100644 index 6438e4a7d4b..00000000000 --- a/demo/demo-zeroconfig-schemadiscovery-registry/pom.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - 4.0.0 - - org.apache.servicecomb.demo - demo-parent - 3.0.0-SNAPSHOT - - demo-zeroconfig-schemadiscovery-registry - Java Chassis::Demo::ZeroConfig-SchemaDiscovery Registry - pom - - demo-zeroconfig-schemadiscovery-registry-server - demo-zeroconfig-schemadiscovery-registry-edge - demo-zeroconfig-schemadiscovery-registry-client - demo-zeroconfig-schemadiscovery-registry-tests - - - - - org.apache.servicecomb - foundation-test-scaffolding - compile - - - org.apache.logging.log4j - log4j-slf4j-impl - - - org.apache.logging.log4j - log4j-api - - - org.apache.logging.log4j - log4j-core - - - diff --git a/demo/demo-zookeeper/README.md b/demo/demo-zookeeper/README.md new file mode 100644 index 00000000000..48a74913f83 --- /dev/null +++ b/demo/demo-zookeeper/README.md @@ -0,0 +1,5 @@ +# Notice + +This integration tests is designed for Zookeeper registry and configuration. And extra test cases include: + +* Test cases related to SpringMVC annotations that demo-springmvc can not cover. diff --git a/demo/demo-zookeeper/consumer/pom.xml b/demo/demo-zookeeper/consumer/pom.xml new file mode 100644 index 00000000000..73fb82a25a3 --- /dev/null +++ b/demo/demo-zookeeper/consumer/pom.xml @@ -0,0 +1,104 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-zookeeper + 3.4.0-SNAPSHOT + + + zookeeper-consumer + Java Chassis::Demo::Zookeeper::CONSUMER + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + registry-zookeeper + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java new file mode 100644 index 00000000000..76fb2fb17db --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.ServerWebSocket; +import io.vertx.core.http.WebSocket; + +@RestSchema(schemaId = "ClientWebsocketController") +@RequestMapping(path = "/ws") +public class ClientWebsocketController { + interface ProviderService { + WebSocket websocket(); + } + + @RpcReference(schemaId = "WebsocketController", microserviceName = "provider") + private ProviderService providerService; + + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + public void websocket(ServerWebSocket serverWebsocket) { + WebSocket providerWebSocket = providerService.websocket(); + providerWebSocket.closeHandler(v -> serverWebsocket.close()); + providerWebSocket.textMessageHandler(m -> { + System.out.println("send message " + m); + serverWebsocket.writeTextMessage(m); + }); + serverWebsocket.textMessageHandler(m -> { + System.out.println("receive message " + m); + providerWebSocket.writeTextMessage(m); + }); + } +} diff --git a/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java new file mode 100644 index 00000000000..3e7a2a0e285 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ConsumerApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsumerApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java new file mode 100644 index 00000000000..e5dd86d9d64 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.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.servicecomb.samples; + +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RestSchema(schemaId = "ConsumerController") +@RequestMapping(path = "/") +public class ConsumerController { + @RpcReference(schemaId = "ProviderController", microserviceName = "provider") + private ProviderService providerService; + + // consumer service which delegate the implementation to provider service. + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { + return providerService.sayHello(name); + } + + @GetMapping("/getConfig") + public String getConfig(@RequestParam("key") String key) { + return providerService.getConfig(key); + } +} diff --git a/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java new file mode 100644 index 00000000000..f0e894d6745 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) +public class ConsumerHeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { + @RpcReference(microserviceName = "provider", schemaId = "HeaderParamWithListSchema") + private IHeaderParamWithListSchemaSpringMvc provider; + + @Override + public String headerListDefault(List headerList) { + return provider.headerListDefault(headerList); + } + + @Override + public String headerListCSV(List headerList) { + return provider.headerListCSV(headerList); + } + + @Override + public String headerListMULTI(List headerList) { + return provider.headerListMULTI(headerList); + } + + @Override + public String headerListSSV(List headerList) { + return provider.headerListSSV(headerList); + } + + @Override + public String headerListPIPES(List headerList) { + return provider.headerListPIPES(headerList); + } +} diff --git a/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java new file mode 100644 index 00000000000..aa169801cf7 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +public class ConsumerReactiveStreamController { + interface ProviderReactiveStreamController { + Publisher sseString(); + + Publisher sseModel(); + } + + @RpcReference(microserviceName = "provider", schemaId = "ReactiveStreamController") + ProviderReactiveStreamController controller; + + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + @Transport(name = CoreConst.RESTFUL) + public Publisher sseString() { + return controller.sseString(); + } + + @GetMapping("/sseModel") + @Transport(name = CoreConst.RESTFUL) + public Publisher sseModel() { + return controller.sseModel(); + } +} diff --git a/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java new file mode 100644 index 00000000000..fe71314c9f4 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +public interface ProviderService { + String sayHello(String name); + + String getConfig(String key); +} diff --git a/demo/demo-zookeeper/consumer/src/main/resources/application.yml b/demo/demo-zookeeper/consumer/src/main/resources/application.yml new file mode 100644 index 00000000000..100797f83b4 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/resources/application.yml @@ -0,0 +1,36 @@ +# +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-zookeeper + version: 0.0.1 + name: consumer + properties: + group: red + registry: + zk: + enabled: true + connectString: 127.0.0.1:2181 + + rest: + address: 0.0.0.0:9092?websocketEnabled=true + server: + websocket-prefix: /ws + + highway: + address: 0.0.0.0:7092 diff --git a/demo/demo-zookeeper/consumer/src/main/resources/log4j2.xml b/demo/demo-zookeeper/consumer/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-zookeeper/gateway/pom.xml b/demo/demo-zookeeper/gateway/pom.xml new file mode 100644 index 00000000000..f2676b1ed6d --- /dev/null +++ b/demo/demo-zookeeper/gateway/pom.xml @@ -0,0 +1,107 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-zookeeper + 3.4.0-SNAPSHOT + + + zookeeper-gateway + Java Chassis::Demo::Zookeeper::GATEWAY + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + edge-core + + + org.apache.servicecomb + registry-zookeeper + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-zookeeper/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java b/demo/demo-zookeeper/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java new file mode 100644 index 00000000000..7d58caafd9d --- /dev/null +++ b/demo/demo-zookeeper/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class GatewayApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-zookeeper/gateway/src/main/resources/application.yml b/demo/demo-zookeeper/gateway/src/main/resources/application.yml new file mode 100644 index 00000000000..ea13ef73d91 --- /dev/null +++ b/demo/demo-zookeeper/gateway/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-zookeeper + version: 0.0.1 + name: gateway + registry: + zk: + enabled: true + connectString: 127.0.0.1:2181 + + rest: + address: 0.0.0.0:9090?websocketEnabled=true + server: + websocket-prefix: /ws + + http: + dispatcher: + edge: + default: + enabled: false + url: + enabled: true + pattern: /(.*) + mappings: + consumer: + prefixSegmentCount: 0 + path: "/.*" + microserviceName: consumer + versionRule: 0.0.0+ + websocket: + mappings: + consumer: + prefixSegmentCount: 0 + path: "/ws/.*" + microserviceName: consumer + versionRule: 0.0.0+ diff --git a/demo/demo-zookeeper/gateway/src/main/resources/log4j2.xml b/demo/demo-zookeeper/gateway/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-zookeeper/gateway/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-zookeeper/pom.xml b/demo/demo-zookeeper/pom.xml new file mode 100644 index 00000000000..aa1030de1ec --- /dev/null +++ b/demo/demo-zookeeper/pom.xml @@ -0,0 +1,61 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-zookeeper + Java Chassis::Demo::Zookeeper + pom + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + solution-basic + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-api + + + + + provider + consumer + gateway + test-client + + + diff --git a/demo/demo-zookeeper/provider/pom.xml b/demo/demo-zookeeper/provider/pom.xml new file mode 100644 index 00000000000..3b008bfb000 --- /dev/null +++ b/demo/demo-zookeeper/provider/pom.xml @@ -0,0 +1,116 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-zookeeper + 3.4.0-SNAPSHOT + + + zookeeper-provider + Java Chassis::Demo::Zookeeper::PROVIDER + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + registry-zookeeper + + + org.apache.servicecomb + config-zookeeper + + + io.reactivex.rxjava3 + rxjava + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java new file mode 100644 index 00000000000..8a773f91c72 --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) +public class HeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { + @Override + public String headerListDefault(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListCSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListMULTI(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListSSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListPIPES(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } +} diff --git a/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java new file mode 100644 index 00000000000..fde6f36b2e3 --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ProviderApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ProviderApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java new file mode 100644 index 00000000000..a238d77be24 --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.StringUtils; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.zookeeper.ZookeeperClient; +import org.apache.servicecomb.config.zookeeper.ZookeeperConfig; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RestSchema(schemaId = "ProviderController") +@RequestMapping(path = "/") +public class ProviderController implements InitializingBean { + private Environment environment; + + private ZookeeperConfig zookeeperConfig; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + this.zookeeperConfig = new ZookeeperConfig(environment); + } + + // a very simple service to echo the request parameter + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { + return "Hello " + name; + } + + @GetMapping("/getConfig") + public String getConfig(@RequestParam("key") String key) { + return environment.getProperty(key); + } + + @Override + public void afterPropertiesSet() throws Exception { + CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConfig.getConnectString(), + zookeeperConfig.getSessionTimeoutMillis(), zookeeperConfig.getConnectionTimeoutMillis(), + new ExponentialBackoffRetry(1000, 3)); + client.start(); + client.blockUntilConnected(10, TimeUnit.SECONDS); + + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = ZookeeperConfig.ZOOKEEPER_DEFAULT_ENVIRONMENT; + } + + String path = String.format(ZookeeperClient.PATH_ENVIRONMENT, env); + if (client.checkExists().forPath(path + "/config.properties") != null) { + client.delete().forPath(path + "/config.properties"); + } + client.create().creatingParentsIfNeeded(). + forPath(path + "/config.properties", "key1=1\nkey2=2".getBytes(StandardCharsets.UTF_8)); + + path = String.format(ZookeeperClient.PATH_VERSION, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment)); + if (client.checkExists().forPath(path + "/config.properties") != null) { + client.delete().forPath(path + "/config.properties"); + } + client.create().creatingParentsIfNeeded(). + forPath(path + "/config.properties", "key2=3\nkey3=4".getBytes(StandardCharsets.UTF_8)); + + path = String.format(ZookeeperClient.PATH_TAG, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment), + zookeeperConfig.getInstanceTag()); + if (client.checkExists().forPath(path + "/config.properties") != null) { + client.delete().forPath(path + "/config.properties"); + } + client.create().creatingParentsIfNeeded(). + forPath(path + "/config.properties", "key2=3\nkey3=5".getBytes(StandardCharsets.UTF_8)); + + client.close(); + } +} diff --git a/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java new file mode 100644 index 00000000000..8108d15fd46 --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.reactivex.rxjava3.core.Flowable; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +@Transport(name = CoreConst.RESTFUL) +public class ReactiveStreamController { + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + public Publisher sseString() { + return Flowable.fromArray("a", "b", "c"); + } + + @GetMapping("/sseModel") + public Publisher sseModel() { + return Flowable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS) + .map(item -> new Model("jack", item.intValue())); + } +} diff --git a/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java new file mode 100644 index 00000000000..6381ef1d54c --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.ServerWebSocket; + +@RestSchema(schemaId = "WebsocketController") +@RequestMapping(path = "/ws") +public class WebsocketController { + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + public void websocket(ServerWebSocket serverWebsocket) { + AtomicInteger receiveCount = new AtomicInteger(0); + CountDownLatch startSend = new CountDownLatch(1); + serverWebsocket.textMessageHandler(s -> { + if ("start".equals(s)) { + startSend.countDown(); + serverWebsocket.writeTextMessage("started"); + return; + } + receiveCount.getAndIncrement(); + }); + serverWebsocket.closeHandler((v) -> System.out.println("closed")); + + new Thread(() -> { + try { + startSend.await(30, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + serverWebsocket.writeTextMessage("hello"); + + for (int i = 0; i < 5; i++) { + serverWebsocket.writeTextMessage("hello " + i); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + serverWebsocket.writeTextMessage("total " + receiveCount.get()); + serverWebsocket.close(); + }).start(); + } +} diff --git a/demo/demo-zookeeper/provider/src/main/resources/application.yml b/demo/demo-zookeeper/provider/src/main/resources/application.yml new file mode 100644 index 00000000000..dd777236bd8 --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +# spring boot configurations +servicecomb: + service: + application: demo-zookeeper + version: 0.0.1 + name: provider + properties: + group: green + registry: + zk: + connectString: 127.0.0.1:2181 + config: + zk: + connectString: 127.0.0.1:2181 + instance-tag: config-demo + + rest: + address: 0.0.0.0:9094?websocketEnabled=true + server: + websocket-prefix: /ws + + highway: + address: 0.0.0.0:7094 + + cors: + enabled: true + origin: "*" + allowCredentials: false + allowedMethod: "*" + maxAge: 3600 diff --git a/demo/demo-zookeeper/provider/src/main/resources/log4j2.xml b/demo/demo-zookeeper/provider/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-zookeeper/test-client/pom.xml b/demo/demo-zookeeper/test-client/pom.xml new file mode 100644 index 00000000000..e527b630005 --- /dev/null +++ b/demo/demo-zookeeper/test-client/pom.xml @@ -0,0 +1,196 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-zookeeper + 3.4.0-SNAPSHOT + + + zookeeper-test-client + Java Chassis::Demo::Zookeeper::TEST-CLIENT + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + registry-local + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + zookeeper:3.8.3 + zookeeper + + alias + + binding to port + + + 2181 + + + + + + zookeeper.port:2181 + + + + + zookeeper-provider:${project.version} + zookeeper-provider + + alias + + + -Dservicecomb.registry.zk.connectString=zookeeper:2181 -Dservicecomb.config.zk.connectString=zookeeper:2181 + + /maven/maven/zookeeper-provider-${project.version}.jar + + + zookeeper:zookeeper + + + ServiceComb is ready + + + 9094 + + + + + + 9094:9094 + + + + + zookeeper-consumer:${project.version} + zookeeper-consumer + + alias + + + -Dservicecomb.registry.zk.connectString=zookeeper:2181 + + /maven/maven/zookeeper-consumer-${project.version}.jar + + + zookeeper:zookeeper + + + ServiceComb is ready + + + 9092 + + + + + + 9092:9092 + + + + + zookeeper-gateway:${project.version} + zookeeper-gateway + + alias + + + -Dservicecomb.registry.zk.connectString=zookeeper:2181 + + /maven/maven/zookeeper-gateway-${project.version}.jar + + + zookeeper:zookeeper + + + ServiceComb is ready + + + 9090 + + + + + + 9090:9090 + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/Config.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/Config.java new file mode 100644 index 00000000000..2e9105cdd4a --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/Config.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +public interface Config { + String GATEWAY_URL = "http://localhost:9090"; +} diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java new file mode 100644 index 00000000000..1b129acc2c6 --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HeaderParamWithListSchemaIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHeaderListDefault(); + testHeaderListMulti(); + testHeaderListCSV(); + testHeaderListSSV(); + testHeaderListPipes(); + } + + // default to multi + private void testHeaderListDefault() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListDefault", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListPipes() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a|b|c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListPIPES", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListSSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a b c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListSSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListCSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a,b,c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + + headers.add("headerList", "a, b, c"); + entity = new HttpEntity<>(headers); + result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListMulti() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListMULTI", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } +} diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java new file mode 100644 index 00000000000..97e883fb45f --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.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.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HelloWorldIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHelloWorld(); + testGetConfig(); + } + + private void testGetConfig() { + String result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key1", String.class); + TestMgr.check("1", result); + result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key2", String.class); + TestMgr.check("3", result); + result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key3", String.class); + TestMgr.check("5", result); + } + + private void testHelloWorld() { + String result = template + .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); + TestMgr.check("Hello World", result); + + // test trace id added + MultiValueMap headers = new HttpHeaders(); + headers.add("X-B3-TraceId", "81de2eb7691c2bbb"); + HttpEntity entity = new HttpEntity(headers); + ResponseEntity response = + template.exchange(Config.GATEWAY_URL + "/sayHello?name=World", HttpMethod.GET, entity, String.class); + TestMgr.check(1, response.getHeaders().get("X-B3-TraceId").size()); + TestMgr.check("81de2eb7691c2bbb", response.getHeaders().getFirst("X-B3-TraceId")); + TestMgr.check("Hello World", response.getBody()); + } +} diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java new file mode 100644 index 00000000000..488a6cf712a --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient.Model; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component +public class ReactiveStreamIT implements CategorizedTestCase { + @Autowired + @Qualifier("reactiveStreamProvider") + ReactiveStreamClient reactiveStreamProvider; + + @Autowired + @Qualifier("reactiveStreamGateway") + ReactiveStreamClient reactiveStreamGateway; + + @Override + public void testRestTransport() throws Exception { + testSseString(reactiveStreamProvider); + testSseModel(reactiveStreamProvider); + testSseString(reactiveStreamGateway); + testSseModel(reactiveStreamGateway); + } + + private void testSseModel(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseModel(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(Model s) { + buffer.append(s.getName()).append(s.getAge()); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("jack0jack1jack2jack3jack4", buffer.toString()); + } + + private void testSseString(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseString(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(String s) { + buffer.append(s); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("abc", buffer.toString()); + } +} diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java new file mode 100644 index 00000000000..26a2a491bbe --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.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.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCaseRunner; +import org.apache.servicecomb.demo.TestMgr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class TestClientApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); + + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args); + + run(); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + } + + public static void run() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); + } +} diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java new file mode 100644 index 00000000000..5f23c66cdac --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.localregistry.RegistryBean; +import org.apache.servicecomb.localregistry.RegistryBean.Instance; +import org.apache.servicecomb.localregistry.RegistryBean.Instances; +import org.apache.servicecomb.provider.pojo.Invoker; +import org.reactivestreams.Publisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.WebSocket; + +@Configuration +public class ThirdSvcConfiguration { + @RequestMapping(path = "/ws") + public interface WebsocketClient { + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + WebSocket websocket(); + } + + @RequestMapping(path = "/") + public interface ReactiveStreamClient { + class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + Publisher sseString(); + + @GetMapping("/sseModel") + Publisher sseModel(); + } + + @Bean + public RegistryBean providerServiceBean() { + return new RegistryBean() + .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) + .setAppId("demo-zookeeper") + .setServiceName("provider") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of( + new Instance().setEndpoints(List.of("rest://localhost:9094"))))); + } + + @Bean + public RegistryBean gatewayServiceBean() { + return new RegistryBean() + .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) + .addSchemaInterface("WebsocketController", WebsocketClient.class) + .setAppId("demo-zookeeper") + .setServiceName("gateway") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of( + new Instance().setEndpoints(List.of("rest://localhost:9090?websocketEnabled=true"))))); + } + + @Bean("reactiveStreamProvider") + public ReactiveStreamClient reactiveStreamProvider() { + return Invoker.createProxy("provider", "ReactiveStreamController", ReactiveStreamClient.class); + } + + @Bean("reactiveStreamGateway") + public ReactiveStreamClient reactiveStreamGateway() { + return Invoker.createProxy("gateway", "ReactiveStreamController", ReactiveStreamClient.class); + } + + @Bean + public WebsocketClient gatewayWebsocketClient() { + return Invoker.createProxy("gateway", "WebsocketController", WebsocketClient.class); + } +} diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java new file mode 100644 index 00000000000..c36f2d16d28 --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.WebsocketClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import io.vertx.core.http.WebSocket; + +@Component +public class WebsocketIT implements CategorizedTestCase { + @Autowired + private WebsocketClient websocketClient; + + @Override + public void testRestTransport() throws Exception { + StringBuffer sb = new StringBuffer(); + AtomicBoolean closed = new AtomicBoolean(false); + CountDownLatch latchStarted = new CountDownLatch(1); + CountDownLatch latch = new CountDownLatch(1); + + WebSocket webSocket = websocketClient.websocket(); + webSocket.textMessageHandler(s -> { + if ("started".equals(s)) { + latchStarted.countDown(); + return; + } + sb.append(s); + sb.append(" "); + webSocket.writeTextMessage(s); + }); + webSocket.closeHandler(v -> { + closed.set(true); + latch.countDown(); + }); + + webSocket.writeTextMessage("start"); + int i = 0; + for (; i < 10; i++) { + if (!latchStarted.await(3, TimeUnit.SECONDS)) { + webSocket.writeTextMessage("start"); + continue; + } + break; + } + TestMgr.check(i < 10, true); + latch.await(30, TimeUnit.SECONDS); + TestMgr.check(sb.toString(), "hello hello 0 hello 1 hello 2 hello 3 hello 4 total 6 "); + TestMgr.check(closed.get(), true); + } +} diff --git a/demo/demo-zookeeper/test-client/src/main/resources/application.yml b/demo/demo-zookeeper/test-client/src/main/resources/application.yml new file mode 100644 index 00000000000..6f8a74f5c9d --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-zookeeper + name: test-client + version: 0.0.1 + + rest: + address: 0.0.0.0:9097 # should be same with server.port to use web container diff --git a/demo/demo-zookeeper/test-client/src/main/resources/log4j2.xml b/demo/demo-zookeeper/test-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-zookeeper/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.java b/demo/demo-zookeeper/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.java new file mode 100644 index 00000000000..549cfe2a300 --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.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.servicecomb.samples; + +import org.apache.servicecomb.demo.TestMgr; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = TestClientApplication.class) +public class ZookeeperIT { + private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperIT.class); + + @BeforeEach + public void setUp() { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + try { + TestClientApplication.run(); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} diff --git a/demo/docker-build-config/pom.xml b/demo/docker-build-config/pom.xml deleted file mode 100644 index fd55e817cb7..00000000000 --- a/demo/docker-build-config/pom.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - demo-parent - org.apache.servicecomb.demo - 3.0.0-SNAPSHOT - - 4.0.0 - - docker-build-config - Java Chassis::Demo::Docker Build Config - pom - - - - - - io.fabric8 - docker-maven-plugin - - - - ${project.artifactId}:${project.version} - ${project.artifactId} - - openjdk:17-alpine - - 7070 - 8080 - - - tar - ${root.basedir}/assembly/assembly.xml - - - java -Xmx128m $JAVA_OPTS -jar $JAR_PATH - - - - - - - - build - package - - build - - - - - - org.commonjava.maven.plugins - directory-maven-plugin - - - directories - - directory-of - - initialize - - root.basedir - - org.apache.servicecomb.demo - demo-parent - - - - - - - - - diff --git a/demo/docker-run-config-edge/pom.xml b/demo/docker-run-config-edge/pom.xml deleted file mode 100644 index 44a792a71c7..00000000000 --- a/demo/docker-run-config-edge/pom.xml +++ /dev/null @@ -1,336 +0,0 @@ - - - - - demo-parent - org.apache.servicecomb.demo - 3.0.0-SNAPSHOT - - 4.0.0 - - docker-run-config-edge - Java Chassis::Demo::Docker Run Config - - pom - - - - - - io.fabric8 - docker-maven-plugin - - - - servicecomb/service-center - service-center - - - server is ready - - - 30100 - - - - - - 30100:30100 - - - - - authentication:${project.version} - authentication - - - - -Dservicecomb.service.registry.address=http://sc.servicecomb.io:30100 - - /maven/maven/authentication-${project.version}.jar - - - service-center:sc.servicecomb.io - - - Register microservice instance success - - - - 7070 - - - - - - 7070:7070 - - - service-center - - - - - business-1-0-0:${project.version} - business-1-0-0 - - - - -Dservicecomb.service.registry.address=http://sc.servicecomb.io:30100 - - /maven/maven/business-1-0-0-${project.version}.jar - - - service-center:sc.servicecomb.io - - - Register microservice instance success - - - - 8080 - - - - - - 8080:8080 - - - service-center - - - - - business-1-1-0:${project.version} - business-1-1-0 - - - - -Dservicecomb.service.registry.address=http://sc.servicecomb.io:30100 - - /maven/maven/business-1-1-0-${project.version}.jar - - - service-center:sc.servicecomb.io - - - Register microservice instance success - - - - 8090 - - - - - - 8090:8090 - - - service-center - - - - - business-2-0-0:${project.version} - business-2-0-0 - - - - -Dservicecomb.service.registry.address=http://sc.servicecomb.io:30100 - - /maven/maven/business-2-0-0-${project.version}.jar - - - service-center:sc.servicecomb.io - - - Register microservice instance success - - - - 8091 - - - - - - 8091:8091 - - - service-center - - - - - edge-service:${project.version} - edge-service - - - - -Dservicecomb.service.registry.address=http://sc.servicecomb.io:30100 -Dservicecomb.service.publishAddress=${docker.hostname} - - /maven/maven/edge-service-${project.version}.jar - - - service-center:sc.servicecomb.io - - - Register microservice instance success - - - - 18090 - - - - - - 18090:18090 - - - service-center - - - - - - - - start - pre-integration-test - - start - - - - stop - post-integration-test - - stop - - - - - - - - - - - docker - - - - org.codehaus.gmavenplus - gmavenplus-plugin - - - add-default-properties - initialize - - execute - - - - - - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - http://${docker.hostname}:30100 - - ${jacoco.failsafe.argLine} - - - - - integration-test - verify - - - - - - - - - docker-machine - - - - org.codehaus.gmavenplus - gmavenplus-plugin - - - add-dynamic-properties - prepare-package - - execute - - - - - - - - - - - - - - diff --git a/demo/docker-run-config-local/pom.xml b/demo/docker-run-config-local/pom.xml deleted file mode 100644 index 577bbec995a..00000000000 --- a/demo/docker-run-config-local/pom.xml +++ /dev/null @@ -1,164 +0,0 @@ - - - - - demo-parent - org.apache.servicecomb.demo - 3.0.0-SNAPSHOT - - 4.0.0 - - docker-run-config-local - Java Chassis::Demo::Docker Run Config Local - - pom - - - - - - io.fabric8 - docker-maven-plugin - - - - ${demo.service.name}:${project.version} - ${demo.service.name} - - - - -Dservicecomb.service.publishAddress=${docker.hostname} - - /maven/maven/${demo.service.name}-${project.version}.jar - - - Register microservice instance success - - - 8080 - - - - - - 8080:8080 - - - - - - - - start - pre-integration-test - - start - - - - stop - post-integration-test - - stop - - - - - - - - - - - docker - - - - org.codehaus.gmavenplus - gmavenplus-plugin - - - add-default-properties - initialize - - execute - - - - - - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - ${jacoco.failsafe.argLine} - - - - - integration-test - verify - - - - - - - - - docker-machine - - - - org.codehaus.gmavenplus - gmavenplus-plugin - - - add-dynamic-properties - prepare-package - - execute - - - - - - - - - - - - - - diff --git a/demo/docker-run-config/pom.xml b/demo/docker-run-config/pom.xml deleted file mode 100644 index b5e714f4915..00000000000 --- a/demo/docker-run-config/pom.xml +++ /dev/null @@ -1,198 +0,0 @@ - - - - - demo-parent - org.apache.servicecomb.demo - 3.0.0-SNAPSHOT - - 4.0.0 - - docker-run-config - Java Chassis::Demo::Docker Run Config - - pom - - - - - - io.fabric8 - docker-maven-plugin - - - - servicecomb/service-center - service-center - - - server is ready - - - 30100 - - - - - - 30100:30100 - - - - - ${demo.service.name}:${project.version} - ${demo.service.name} - - - - -Dservicecomb.service.registry.address=http://sc.servicecomb.io:30100 -Dservicecomb.service.publishAddress=${docker.hostname} - - /maven/maven/${demo.service.name}-${project.version}.jar - - - service-center:sc.servicecomb.io - - - Register microservice instance success - - - - - 8080 - - - - - - 7070:7070 - 8080:8080 - - - service-center - - - - - - - - start - pre-integration-test - - start - - - - stop - post-integration-test - - stop - - - - - - - - - - - docker - - - - org.codehaus.gmavenplus - gmavenplus-plugin - - - add-default-properties - initialize - - execute - - - - - - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - http://${docker.hostname}:30100 - - ${jacoco.failsafe.argLine} - - - - - integration-test - verify - - - - - - - - - docker-machine - - - - org.codehaus.gmavenplus - gmavenplus-plugin - - - add-dynamic-properties - prepare-package - - execute - - - - - - - - - - - - - - diff --git a/demo/pom.xml b/demo/pom.xml index dd2b82b67b3..d0376883c6b 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default org.apache.servicecomb.demo @@ -35,10 +35,6 @@ pom - docker-build-config - docker-run-config - docker-run-config-edge - docker-run-config-local demo-schema demo-pojo demo-filter @@ -48,23 +44,22 @@ demo-register-url-prefix demo-local-registry demo-multi-registries - demo-zeroconfig-schemadiscovery-registry + demo-zeroconfig-registry demo-spring-boot-transport demo-edge demo-multiple demo-multi-service-center - demo-signature demo-cse-v1 demo-cse-v2 + demo-nacos + + + demo-zookeeper + demo-consul - - org.apache.servicecomb.demo - demo-signature - ${project.version} - org.apache.servicecomb.demo demo-schema @@ -134,10 +129,6 @@ - - org.codehaus.groovy - groovy - org.junit.jupiter junit-jupiter @@ -148,12 +139,6 @@ - - com.github.odavid.maven.plugins - mixin-maven-plugin - 0.1-alpha-40 - true - org.commonjava.maven.plugins directory-maven-plugin @@ -190,7 +175,59 @@ + + org.commonjava.maven.plugins + directory-maven-plugin + + + directories + + directory-of + + initialize + + root.basedir + + org.apache.servicecomb.demo + demo-parent + + + + + + + + org.commonjava.maven.plugins + directory-maven-plugin + + + + + + docker + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + ${jacoco.failsafe.argLine} + + + + + integration-test + verify + + + + + + + + diff --git a/dependencies/bom/pom.xml b/dependencies/bom/pom.xml index 83377acd7f7..ed5e1bc91a2 100644 --- a/dependencies/bom/pom.xml +++ b/dependencies/bom/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb java-chassis-dependencies-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT java-chassis-bom @@ -90,13 +90,6 @@ ${project.version} - - - org.apache.servicecomb - deployment - ${project.version} - - org.apache.servicecomb @@ -118,6 +111,16 @@ config-nacos ${project.version} + + org.apache.servicecomb + config-zookeeper + ${project.version} + + + org.apache.servicecomb + config-consul + ${project.version} + @@ -216,14 +219,8 @@ handler-tracing-zipkin ${project.version} - - - org.apache.servicecomb - inspector - ${project.version} - - + org.apache.servicecomb metrics-core @@ -268,47 +265,57 @@ org.apache.servicecomb - registry-schema-discovery + registry-service-center ${project.version} org.apache.servicecomb - registry-service-center + registry-zero-config ${project.version} org.apache.servicecomb - registry-zero-config + registry-nacos ${project.version} - org.apache.servicecomb - solution-basic + registry-zookeeper ${project.version} - org.apache.servicecomb - java-chassis-spring-boot-starter + registry-etcd ${project.version} org.apache.servicecomb - java-chassis-spring-boot-starter-servlet + config-etcd ${project.version} + + org.apache.servicecomb + registry-consul + ${project.version} + + org.apache.servicecomb - java-chassis-spring-boot-starter-standalone + solution-basic + ${project.version} + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-servlet ${project.version} - org.apache.servicecomb - servicestage-environment + java-chassis-spring-boot-starter-standalone ${project.version} + org.apache.servicecomb servicestage @@ -404,4 +411,4 @@ - \ No newline at end of file + diff --git a/dependencies/default/pom.xml b/dependencies/default/pom.xml index f89b097b70b..22f2190ddd1 100644 --- a/dependencies/default/pom.xml +++ b/dependencies/default/pom.xml @@ -17,12 +17,12 @@ --> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.apache.servicecomb java-chassis-dependencies-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT java-chassis-dependencies @@ -32,73 +32,64 @@ 1.2.2 - 0.7.7 - 2.5.3 - 1.9.19 - 3.24.2 - 4.2.0 - 5.15.1 - 1.9.4 - 1.10 - 2.11.0 + 3.27.7 + 4.3.0 + 6.3.0 + 1.11.0 + 2.21.0 2.6 - 3.12.0 - 1.2 - 1.10.0 - 1.5.1 - 4.0.2 - 1.0.1 + 3.20.0 + 1.3.5 + 1.15.0 + 5.9.0 + 1.0.3 3.0.2 1.17.12 1.17.13 - 31.1-jre + 33.5.0-jre 5.1.0 1.3 - 2.1.12 - 6.2.5.Final + 2.2.2 + 9.1.0.Final 4.5.14 1.5.18 - 2.14.2 - 1.5.3 + 2.20.1 + 1.6.0 1.3.2 1 1.5 - 2.1.6 - 2.39 + 4.0.2 1.34 4.13.2 - 5.9.2 + 5.13.4 1.2.17 - 2.20.0 - 1.2.11 - 3.9.1 - 1.10.6 + 2.25.3 + 3.9.12 + 1.16.4 + 5.21.0 + 5.2.0 5.15.0 - 2.2.2 + 3.1.1 0.3.0 - 4.1.92.Final - 4.11.0 0.16.0 - 3.22.3 + 3.23.4 1.8.0 2.2.27 + 1.0.4 1.7.0 2.7.18 - 1.3.8 - 0.5.1 + 3.1.12 1.0.0 0.13.2 - 4.0.4 + 6.0.0 1.7.36 - 1.33 - 1.6.3 - 5.3.25 - 2.7.9 - 1.6.10 - 1.3.3 - 4.4.1 - 2.24.1 - 2.16.3 + 2.5 + 2.2.41 + 5.0.8 + 3.5.1 + 3.4.0 + 0.8.6 + 1.8.0 ${basedir}/../.. @@ -106,29 +97,6 @@ - - ch.qos.logback - logback-classic - ${logback.version} - - - ch.qos.logback - logback-core - ${logback.version} - - - - com.esotericsoftware - kryo-shaded - ${esotericsoftware.version} - - - - com.fasterxml - classmate - ${classmate.version} - - com.fasterxml.jackson jackson-bom @@ -137,6 +105,20 @@ pom + + org.mockito + mockito-bom + ${mockito.version} + import + pom + + + org.mockito + mockito-inline + ${mockito-inline.version} + test + + com.github.seanyinx unit-scaffolding @@ -144,6 +126,22 @@ test + + org.apache.curator + curator-x-discovery + ${curator.version} + + + org.apache.curator + curator-framework + ${curator.version} + + + org.apache.curator + curator-recipes + ${curator.version} + + com.google.code.findbugs jsr305 @@ -185,129 +183,12 @@ ${guice.version} + com.google.protobuf protobuf-java ${protobuf.version} - - - - com.netflix.archaius - archaius-core - ${archaius.version} - - - com.google.code.findbugs - annotations - - - - - - com.netflix.governator - governator-annotations - ${governator-annotations.version} - - - com.netflix.governator - governator-api - ${governator-api.version} - - - - com.netflix.hystrix - hystrix-core - ${hystrix.version} - - - - com.netflix.netflix-commons - netflix-eventbus - ${netflix-commons.version} - - - javax.servlet - servlet-api - - - - - com.netflix.netflix-commons - netflix-eventbus-rx - ${netflix-commons.version} - - - javax.servlet - servlet-api - - - com.netflix.rxjava - rxjava-core - - - - - - com.netflix.ribbon - ribbon - ${ribbon.version} - - - com.netflix.ribbon - ribbon-archaius - ${ribbon.version} - - - com.netflix.ribbon - ribbon-core - ${ribbon.version} - - - com.google.code.findbugs - annotations - - - - - com.netflix.ribbon - ribbon-loadbalancer - ${ribbon.version} - - - com.google.code.findbugs - annotations - - - - - - com.netflix.servo - servo-core - ${servo.version} - - - com.google.code.findbugs - annotations - - - - - - com.netflix.spectator - spectator-reg-servo - ${spectator.version} - - - - com.squareup.okhttp3 - okhttp - ${okhttp3.version} - - - - com.squareup.okhttp3 - mockwebserver - ${okhttp3.version} + test @@ -316,12 +197,6 @@ ${commons-beanutils.version} - - commons-configuration - commons-configuration - ${commons-configuration.version} - - commons-io commons-io @@ -340,56 +215,6 @@ ${commons-logging.version} - - io.github.swagger2markup - swagger2markup - ${swagger2markup.version} - - - io.swagger - swagger-compat-spec-parser - - - commons-collections - commons-collections - - - - - - io.netty - netty-bom - import - ${netty.version} - pom - - - - io.reactivex - rxjava - ${rxjava.version} - - - io.reactivex - rxnetty - ${rxnetty.version} - - - - io.netty - netty-codec-http - - - io.netty - netty-handler - - - io.netty - netty-transport-native-epoll - - - - org.apache.maven maven-model @@ -438,78 +263,33 @@ - io.swagger - swagger-core + io.swagger.core.v3 + swagger-core-jakarta ${swagger.version} - - - - javax.validation - validation-api - - - io.vertx - vertx-codegen - ${vertx.version} - - - io.vertx - vertx-core - ${vertx.version} + io.reactivex.rxjava3 + rxjava + ${rxjava.version} - io.vertx - vertx-web - ${vertx.version} + org.reactivestreams + reactive-streams + ${reactive-streams.version} io.zipkin.brave - brave - ${brave.version} - - - io.zipkin.brave - brave-context-slf4j - ${brave.version} - - - io.zipkin.brave - brave-instrumentation-jaxrs2 - ${brave.version} - - - io.zipkin.brave - brave-instrumentation-servlet - ${brave.version} - - - io.zipkin.brave - brave-instrumentation-spring-web - ${brave.version} - - - io.zipkin.brave - brave-instrumentation-spring-webmvc - ${brave.version} - - - io.zipkin.brave - brave-spring-beans + brave-bom ${brave.version} + pom + import io.zipkin.reporter2 - zipkin-reporter - ${zipkin-reporter.version} - - - io.zipkin.reporter2 - zipkin-sender-okhttp3 + zipkin-reporter-bom ${zipkin-reporter.version} @@ -547,12 +327,6 @@ ${javax-resource.version} - - jakarta.ws.rs - jakarta.ws.rs-api - ${jakarta.ws.rs.version} - - junit junit @@ -601,24 +375,6 @@ ${log4j2.version} - - org.asciidoctor - asciidoctorj - ${asciidoctorj.version} - - - com.jcraft - jzlib - - - - - - org.aspectj - aspectjweaver - ${aspectj.version} - - org.awaitility awaitility @@ -682,13 +438,6 @@ test - - org.mock-server - mockserver-netty - ${mock-server.version} - test - - org.slf4j slf4j-api @@ -696,9 +445,9 @@ - org.springframework - spring-framework-bom - ${spring.version} + io.vertx + vertx-dependencies + ${vertx.version} pom import @@ -744,6 +493,18 @@ pom import + + + io.etcd + jetcd-core + ${jetcd-core.version} + + + + org.kiwiproject + consul-client + ${consul-client.version} + diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 8cbf8e890fb..4ee9c9ea071 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb java-chassis - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT java-chassis-dependencies-parent diff --git a/deployment/README.md b/deployment/README.md deleted file mode 100644 index b2764cc0a49..00000000000 --- a/deployment/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# About Deployment Module - -Deployment is a mechanism to manage how microservics interact with external services. When running a microservice in a Cloud Native environment, it will interact with many external services, like service center, config center, an APM service to report tracing data, a dashboard service to report health data, etc. Deployment Service manages the meta data of these services. Deployment queries the addresses , the parameters, the authentication information of these services and so on. - -Deployment Service is some kind of service like service center, they are differ from two aspects: -1. All microservics will report their information to service center. But Deployment Service acts as a management system and knows the meta information in deploy time. -2. Service center provide some other functions like instance management and heartbeat, while Deployment Service only provides metadata query services and it is simple. - -This module does not provide a Deployment Service, it provides the interface to interacts with Deployment Service. Service providers can implement the interface. - -They are some design constraints need to be considered when implement Deployment interface: -1. Deployment can only read configurations of environment, microservics.yaml. It can not read dynamic configurations. -2. Deployment is initialized before bean initialization. diff --git a/deployment/pom.xml b/deployment/pom.xml deleted file mode 100644 index eb09e872880..00000000000 --- a/deployment/pom.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - org.apache.servicecomb - java-chassis-parent - 3.0.0-SNAPSHOT - ../parents/default - - 4.0.0 - - deployment - Java Chassis::Deployment - - - - org.apache.servicecomb - foundation-config - - - org.apache.servicecomb - foundation-vertx - - - org.apache.servicecomb - foundation-common - - - - io.vertx - vertx-codegen - provided - - - - org.apache.logging.log4j - log4j-slf4j-impl - test - - - org.apache.logging.log4j - log4j-core - test - - - org.apache.servicecomb - foundation-test-scaffolding - - - org.apache.commons - commons-lang3 - - - diff --git a/deployment/src/main/java/org/apache/servicecomb/deployment/Deployment.java b/deployment/src/main/java/org/apache/servicecomb/deployment/Deployment.java deleted file mode 100644 index 2a35110d999..00000000000 --- a/deployment/src/main/java/org/apache/servicecomb/deployment/Deployment.java +++ /dev/null @@ -1,35 +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.servicecomb.deployment; - -import java.util.List; - -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; - -public class Deployment { - private static final List providerList = SPIServiceUtils.getSortedService(DeploymentProvider.class); - - public static SystemBootstrapInfo getSystemBootStrapInfo(String systemKey) { - for (DeploymentProvider provider : providerList) { - if (provider.getSystemBootStrapInfo(systemKey) != null) { - return provider.getSystemBootStrapInfo(systemKey); - } - } - return null; - } -} diff --git a/deployment/src/main/java/org/apache/servicecomb/deployment/DeploymentProvider.java b/deployment/src/main/java/org/apache/servicecomb/deployment/DeploymentProvider.java deleted file mode 100644 index e2e5396e590..00000000000 --- a/deployment/src/main/java/org/apache/servicecomb/deployment/DeploymentProvider.java +++ /dev/null @@ -1,27 +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.servicecomb.deployment; - -public interface DeploymentProvider { - - default int getOrder() { - return 100; - } - - SystemBootstrapInfo getSystemBootStrapInfo(String systemKey); -} diff --git a/deployment/src/main/java/org/apache/servicecomb/deployment/SystemBootstrapInfo.java b/deployment/src/main/java/org/apache/servicecomb/deployment/SystemBootstrapInfo.java deleted file mode 100644 index c98f93e6a64..00000000000 --- a/deployment/src/main/java/org/apache/servicecomb/deployment/SystemBootstrapInfo.java +++ /dev/null @@ -1,47 +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.servicecomb.deployment; - -import java.util.List; -import java.util.Map; - -public class SystemBootstrapInfo { - private List accessURL; - - private Map properties; - - public List getAccessURL() { - return accessURL; - } - - public void setAccessURL(List accessURL) { - this.accessURL = accessURL; - } - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - - public Object getProperty(String key) { - return properties.get(key); - } -} diff --git a/deployment/src/test/java/org/apache/servicecomb/deployment/CustomDeploymentProvider.java b/deployment/src/test/java/org/apache/servicecomb/deployment/CustomDeploymentProvider.java deleted file mode 100644 index 43c0a717710..00000000000 --- a/deployment/src/test/java/org/apache/servicecomb/deployment/CustomDeploymentProvider.java +++ /dev/null @@ -1,48 +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.servicecomb.deployment; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.servicecomb.config.ConfigUtil; - -import java.util.Arrays; - -public class CustomDeploymentProvider implements DeploymentProvider { - private static AbstractConfiguration configuration = ConfigUtil.createLocalConfig(); - - @Override - public int getOrder() { - return 0; - } - - @Override - public SystemBootstrapInfo getSystemBootStrapInfo(String systemKey) { - if (!systemKey.equals("TestCenter")) { - return null; - } - SystemBootstrapInfo cc = new SystemBootstrapInfo(); - cc.setAccessURL(Arrays.asList("http://localhost/custom")); - return cc; - } - - @VisibleForTesting - public static void setConfiguration(AbstractConfiguration configuration) { - CustomDeploymentProvider.configuration = configuration; - } -} diff --git a/deployment/src/test/java/org/apache/servicecomb/deployment/TestDeployment.java b/deployment/src/test/java/org/apache/servicecomb/deployment/TestDeployment.java deleted file mode 100644 index 41d6476bd3e..00000000000 --- a/deployment/src/test/java/org/apache/servicecomb/deployment/TestDeployment.java +++ /dev/null @@ -1,39 +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.servicecomb.deployment; - -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestDeployment { - @AfterEach - public void tearDown() { - ArchaiusUtils.resetConfig(); - } - - @Test - public void testConfiguration() { - CustomDeploymentProvider.setConfiguration(ConfigUtil.createLocalConfig()); - SystemBootstrapInfo info = Deployment.getSystemBootStrapInfo("TestCenter"); - Assertions.assertEquals("http://localhost/custom", info.getAccessURL().get(0)); - Assertions.assertNull(Deployment.getSystemBootStrapInfo("wrong")); - } -} diff --git a/deployment/src/test/resources/META-INF/services/org.apache.servicecomb.deployment.DeploymentProvider b/deployment/src/test/resources/META-INF/services/org.apache.servicecomb.deployment.DeploymentProvider deleted file mode 100644 index d07cc1e76c4..00000000000 --- a/deployment/src/test/resources/META-INF/services/org.apache.servicecomb.deployment.DeploymentProvider +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.deployment.CustomDeploymentProvider diff --git a/distribution/pom.xml b/distribution/pom.xml index 2c9617372f0..ddd1e3de25d 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default apache-servicecomb-java-chassis-distribution @@ -54,6 +54,10 @@ org.apache.servicecomb service-center-client + + org.apache.servicecomb + registry-nacos + @@ -75,12 +79,6 @@ java-chassis-core - - - org.apache.servicecomb - deployment - - org.apache.servicecomb @@ -179,12 +177,6 @@ handler-tracing-zipkin - - - org.apache.servicecomb - inspector - - org.apache.servicecomb @@ -222,10 +214,6 @@ org.apache.servicecomb registry-local - - org.apache.servicecomb - registry-schema-discovery - org.apache.servicecomb registry-service-center @@ -242,10 +230,6 @@ - - org.apache.servicecomb - java-chassis-spring-boot-starter - org.apache.servicecomb java-chassis-spring-boot-starter-servlet @@ -256,11 +240,6 @@ - - org.apache.servicecomb - servicestage-environment - - org.apache.servicecomb servicestage diff --git a/distribution/src/release/NOTICE b/distribution/src/release/NOTICE index d1821a3491c..5ed927047d5 100644 --- a/distribution/src/release/NOTICE +++ b/distribution/src/release/NOTICE @@ -1,6 +1,5 @@ Apache ServiceComb Java Chassis -Copyright 2017-2019 The Apache Software Foundation +Copyright 2017-2024 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). - diff --git a/distribution/src/release/licenses/LICENSE-abego b/distribution/src/release/licenses/LICENSE-abego index a4fe241d454..1620af43b30 100644 --- a/distribution/src/release/licenses/LICENSE-abego +++ b/distribution/src/release/licenses/LICENSE-abego @@ -2,26 +2,26 @@ Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org) All rights reserved. -Redistribution and use in source and binary forms, with or without +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -3. Neither the name of the abego Software GmbH nor the names of its - contributors may be used to endorse or promote products derived from this +3. Neither the name of the abego Software GmbH nor the names of its + contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/distribution/src/release/licenses/LICENSE-animalsniffer b/distribution/src/release/licenses/LICENSE-animalsniffer index 719e6d7f0fd..4e28c7bc322 100644 --- a/distribution/src/release/licenses/LICENSE-animalsniffer +++ b/distribution/src/release/licenses/LICENSE-animalsniffer @@ -18,4 +18,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. + THE SOFTWARE. diff --git a/distribution/src/release/licenses/LICENSE-checkerqual b/distribution/src/release/licenses/LICENSE-checkerqual index f6743bb6980..7f197e96dec 100644 --- a/distribution/src/release/licenses/LICENSE-checkerqual +++ b/distribution/src/release/licenses/LICENSE-checkerqual @@ -17,4 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/distribution/src/release/licenses/LICENSE-edl-v10 b/distribution/src/release/licenses/LICENSE-edl-v10 index a4a267ef07f..90202907fd2 100644 --- a/distribution/src/release/licenses/LICENSE-edl-v10 +++ b/distribution/src/release/licenses/LICENSE-edl-v10 @@ -9,4 +9,4 @@ Redistribution and use in source and binary forms, with or without modification, Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/distribution/src/release/licenses/LICENSE-epl-v10 b/distribution/src/release/licenses/LICENSE-epl-v10 index 3fa00836fa4..b0476ee93e8 100644 --- a/distribution/src/release/licenses/LICENSE-epl-v10 +++ b/distribution/src/release/licenses/LICENSE-epl-v10 @@ -83,4 +83,4 @@ All Recipient's rights under this Agreement shall terminate if it fails to compl Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. -This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. \ No newline at end of file +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. diff --git a/distribution/src/release/licenses/LICENSE-jcodings b/distribution/src/release/licenses/LICENSE-jcodings index f8031949305..f85e365de02 100644 --- a/distribution/src/release/licenses/LICENSE-jcodings +++ b/distribution/src/release/licenses/LICENSE-jcodings @@ -15,4 +15,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/distribution/src/release/licenses/LICENSE-jsonp b/distribution/src/release/licenses/LICENSE-jsonp index 07368a285ae..75372e3d2c9 100644 --- a/distribution/src/release/licenses/LICENSE-jsonp +++ b/distribution/src/release/licenses/LICENSE-jsonp @@ -1,5 +1,5 @@ 1. Definitions. - + 1.1. "Contributor" means each individual or entity that creates or contributes to the creation of Modifications. diff --git a/distribution/src/release/licenses/LICENSE-logback b/distribution/src/release/licenses/LICENSE-logback index 8953762a3cd..1f99506bfa4 100644 --- a/distribution/src/release/licenses/LICENSE-logback +++ b/distribution/src/release/licenses/LICENSE-logback @@ -11,4 +11,4 @@ the Eclipse Foundation or (per the licensee's choosing) under the terms of the GNU Lesser General Public License version 2.1 -as published by the Free Software Foundation. \ No newline at end of file +as published by the Free Software Foundation. diff --git a/distribution/src/release/licenses/NOTICE-protostuff b/distribution/src/release/licenses/NOTICE-protostuff index e1c9c379777..1f95779a7fb 100644 --- a/distribution/src/release/licenses/NOTICE-protostuff +++ b/distribution/src/release/licenses/NOTICE-protostuff @@ -3,13 +3,13 @@ Copyright 2009 David Yu dyuproject@gmail.com ============================================================== -protobuf is copyright Google inc unless otherwise noted. +protobuf is copyright Google inc unless otherwise noted. It is licensed under the BSD license. -jackson-core-asl is copyright FasterXml unless otherwise noted. +jackson-core-asl is copyright FasterXml unless otherwise noted. It is licensed under the apache 2.0 license. -antlr is copyright Terence Parr unless otherwise noted. +antlr is copyright Terence Parr unless otherwise noted. It is licensed under the BSD license. stringtemplate is copyright Terence Parr unless otherwise noted. @@ -17,10 +17,10 @@ It is licensed under the BSD license. velocity is licensed under the apache 2.0 license. -B64Code.java is copyright Mort Bay Consulting Pty Ltd unless otherwise noted. +B64Code.java is copyright Mort Bay Consulting Pty Ltd unless otherwise noted. It is licensed under the apache 2.0 license. -jarjar is copyright Google inc unless otherwise noted. +jarjar is copyright Google inc unless otherwise noted. It is licensed under the apache 2.0 license. guava is copyright Google inc unless otherwise noted. diff --git a/doap_ServiceComb_Java-Chassis.rdf b/doap_ServiceComb_Java-Chassis.rdf deleted file mode 100644 index f5dbe8a1ced..00000000000 --- a/doap_ServiceComb_Java-Chassis.rdf +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - 2021-03-25 - - Apache ServiceComb Java-Chassis - - - ServiceComb Java Chassis is a Software Development Kit (SDK) for rapid development of microservices in Java, providing service registration, service discovery, dynamic routing, and service management features - ServiceComb Java Chassis is a Software Development Kit (SDK) for rapid development of microservices in Java, providing service registration, service discovery, dynamic routing, and service management features - - - - Java - - - - ServiceComb Java-Chassis 2.2.0 - 2021-02-28 - 2.2.0 - - - - - - - - - - - liubao68 - - - - - - diff --git a/dynamic-config/config-apollo/pom.xml b/dynamic-config/config-apollo/pom.xml index 65114c20034..7cc03d182dd 100644 --- a/dynamic-config/config-apollo/pom.xml +++ b/dynamic-config/config-apollo/pom.xml @@ -22,7 +22,7 @@ dynamic-config org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 config-apollo diff --git a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/client/ApolloClient.java b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloClient.java similarity index 78% rename from dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/client/ApolloClient.java rename to dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloClient.java index 9dc040c1561..61c6a9c5c7d 100644 --- a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/client/ApolloClient.java +++ b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloClient.java @@ -15,11 +15,11 @@ * limitations under the License. */ -package org.apache.servicecomb.config.client; +package org.apache.servicecomb.config.apollo; -import static org.apache.servicecomb.config.client.ConfigurationAction.CREATE; -import static org.apache.servicecomb.config.client.ConfigurationAction.DELETE; -import static org.apache.servicecomb.config.client.ConfigurationAction.SET; +import static org.apache.servicecomb.config.apollo.ConfigurationAction.CREATE; +import static org.apache.servicecomb.config.apollo.ConfigurationAction.DELETE; +import static org.apache.servicecomb.config.apollo.ConfigurationAction.SET; import java.io.IOException; import java.util.HashMap; @@ -29,8 +29,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import com.google.common.annotations.VisibleForTesting; -import org.apache.servicecomb.config.archaius.sources.ApolloConfigurationSourceImpl.UpdateHandler; +import org.apache.servicecomb.config.apollo.ApolloDynamicPropertiesSource.UpdateHandler; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,6 +40,7 @@ import org.springframework.web.client.RestTemplate; import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.annotations.VisibleForTesting; import io.netty.handler.codec.http.HttpResponseStatus; @@ -48,34 +48,19 @@ public class ApolloClient { private static final Logger LOGGER = LoggerFactory.getLogger(ApolloClient.class); - private static final ApolloConfig APOLLO_CONFIG = ApolloConfig.INSTANCE; - private static final Map originalConfigMap = new ConcurrentHashMap<>(); private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(1); - private final int refreshInterval = APOLLO_CONFIG.getRefreshInterval(); - - private final int firstRefreshInterval = APOLLO_CONFIG.getFirstRefreshInterval(); - - private final String serviceUri = APOLLO_CONFIG.getServerUri(); - - private final String serviceName = APOLLO_CONFIG.getServiceName(); - - private final String token = APOLLO_CONFIG.getToken(); - - private final String env = APOLLO_CONFIG.getEnv(); - - private final String clusters = APOLLO_CONFIG.getServerClusters(); - - private final String namespace = APOLLO_CONFIG.getNamespace(); + private final ApolloConfig apolloConfig; private final UpdateHandler updateHandler; private static RestTemplate rest = new RestTemplate(); - public ApolloClient(UpdateHandler updateHandler) { + public ApolloClient(UpdateHandler updateHandler, ApolloConfig apolloConfig) { this.updateHandler = updateHandler; + this.apolloConfig = apolloConfig; } @VisibleForTesting @@ -89,8 +74,9 @@ static void setRest(RestTemplate rest) { } public void refreshApolloConfig() { - EXECUTOR - .scheduleWithFixedDelay(new ConfigRefresh(serviceUri), firstRefreshInterval, refreshInterval, TimeUnit.SECONDS); + EXECUTOR.scheduleWithFixedDelay(new ConfigRefresh(apolloConfig.getServerUri()), + apolloConfig.getFirstRefreshInterval(), + apolloConfig.getRefreshInterval(), TimeUnit.SECONDS); } class ConfigRefresh implements Runnable { @@ -113,7 +99,7 @@ public void run() { void refreshConfig() { HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "application/json;charset=UTF-8"); - headers.add("Authorization", token); + headers.add("Authorization", apolloConfig.getToken()); HttpEntity entity = new HttpEntity<>(headers); ResponseEntity exchange = rest.exchange(composeAPI(), HttpMethod.GET, entity, String.class); if (HttpResponseStatus.OK.code() == exchange.getStatusCode().value()) { @@ -127,16 +113,16 @@ void refreshConfig() { } } else { LOGGER.error("fetch configuration failed, error code:{} for {}", - exchange.getStatusCodeValue(), + exchange.getStatusCode().value(), exchange.getBody()); } } private String composeAPI() { - String api = serviceUri + "/openapi/v1/envs/" + env + - "/apps/" + serviceName + - "/clusters/" + clusters + - "/namespaces/" + namespace + + String api = serviceUri + "/openapi/v1/envs/" + apolloConfig.getEnv() + + "/apps/" + apolloConfig.getServiceName() + + "/clusters/" + apolloConfig.getServerClusters() + + "/namespaces/" + apolloConfig.getNamespace() + "/releases/latest"; return api; } @@ -177,4 +163,3 @@ void compareChangedConfig(Map before, Map after) } } } - diff --git a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloConfig.java b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloConfig.java new file mode 100644 index 00000000000..97509b9ee15 --- /dev/null +++ b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloConfig.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.servicecomb.config.apollo; + +import org.springframework.core.env.Environment; + +public class ApolloConfig { + private static final String SERVER_URL_KEY = "apollo.config.serverUri"; + + private static final String SERVER_NAMESPACE = "apollo.config.namespace"; + + private static final String SERVER_ENV = "apollo.config.env"; + + private static final String SERVER_CLUSTERS = "apollo.config.clusters"; + + private static final String APOLLO_SERVICE_NAME = "apollo.config.serviceName"; + + private static final String TOKEN = "apollo.config.token"; + + private static final String REFRESH_INTERVAL = "apollo.config.refreshInterval"; + + private static final String FIRST_REFRESH_INTERVAL = "apollo.config.firstRefreshInterval"; + + private static final int DEFAULT_REFRESH_INTERVAL = 3; + + private static final int DEFAULT_FIRST_REFRESH_INTERVAL = 0; + + private final Environment environment; + + public ApolloConfig(Environment environment) { + this.environment = environment; + } + + public String getServiceName() { + return environment.getProperty(APOLLO_SERVICE_NAME); + } + + public String getServerUri() { + return environment.getProperty(SERVER_URL_KEY); + } + + public String getToken() { + return environment.getProperty(TOKEN); + } + + public String getEnv() { + return environment.getProperty(SERVER_ENV); + } + + public String getNamespace() { + return environment.getProperty(SERVER_NAMESPACE); + } + + public String getServerClusters() { + return environment.getProperty(SERVER_CLUSTERS); + } + + public int getRefreshInterval() { + return environment.getProperty(REFRESH_INTERVAL, int.class, DEFAULT_REFRESH_INTERVAL); + } + + public int getFirstRefreshInterval() { + return environment.getProperty(FIRST_REFRESH_INTERVAL, int.class, DEFAULT_FIRST_REFRESH_INTERVAL); + } +} diff --git a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloConfiguration.java b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloConfiguration.java new file mode 100644 index 00000000000..152f23962c2 --- /dev/null +++ b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloConfiguration.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.apollo; + +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ApolloConfiguration { + +} diff --git a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloDynamicPropertiesSource.java b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloDynamicPropertiesSource.java new file mode 100644 index 00000000000..4a1113283e0 --- /dev/null +++ b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloDynamicPropertiesSource.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.servicecomb.config.apollo; + +import static org.apache.servicecomb.config.apollo.ConfigurationAction.CREATE; +import static org.apache.servicecomb.config.apollo.ConfigurationAction.DELETE; +import static org.apache.servicecomb.config.apollo.ConfigurationAction.SET; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.servicecomb.config.ConfigMapping; +import org.apache.servicecomb.config.DynamicPropertiesSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +public class ApolloDynamicPropertiesSource implements DynamicPropertiesSource { + public static final String SOURCE_NAME = "apollo"; + + private static final Logger LOGGER = LoggerFactory.getLogger(ApolloDynamicPropertiesSource.class); + + private final Map valueCache = new ConcurrentHashMap<>(); + + private final ApolloDynamicPropertiesSource.UpdateHandler updateHandler = + new ApolloDynamicPropertiesSource.UpdateHandler(); + + public ApolloDynamicPropertiesSource() { + } + + @Override + public int getOrder() { + return 300; + } + + private void init(Environment environment) { + ApolloConfig apolloConfig = new ApolloConfig(environment); + ApolloClient apolloClient = new ApolloClient(updateHandler, apolloConfig); + apolloClient.refreshApolloConfig(); + } + + public class UpdateHandler { + public void handle(ConfigurationAction action, Map config) { + if (config == null || config.isEmpty()) { + return; + } + Map configuration = ConfigMapping.getConvertedMap(config); + if (CREATE.equals(action)) { + valueCache.putAll(configuration); + } else if (SET.equals(action)) { + valueCache.putAll(configuration); + } else if (DELETE.equals(action)) { + configuration.keySet().forEach(valueCache::remove); + } else { + LOGGER.error("action: {} is invalid.", action.name()); + return; + } + LOGGER.warn("Config value cache changed: action:{}; item:{}", action.name(), configuration.keySet()); + } + } + + @Override + public PropertySource create(Environment environment) { + init(environment); + return new MapPropertySource(SOURCE_NAME, valueCache); + } +} diff --git a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ConfigurationAction.java b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ConfigurationAction.java new file mode 100644 index 00000000000..1a0047152b2 --- /dev/null +++ b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ConfigurationAction.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config.apollo; + +public enum ConfigurationAction { + CREATE, + SET, + DELETE +} diff --git a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/archaius/sources/ApolloConfigurationSourceImpl.java b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/archaius/sources/ApolloConfigurationSourceImpl.java deleted file mode 100644 index 3b261249f19..00000000000 --- a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/archaius/sources/ApolloConfigurationSourceImpl.java +++ /dev/null @@ -1,147 +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.servicecomb.config.archaius.sources; - -import static com.netflix.config.WatchedUpdateResult.createIncremental; -import static org.apache.servicecomb.config.client.ConfigurationAction.CREATE; -import static org.apache.servicecomb.config.client.ConfigurationAction.DELETE; -import static org.apache.servicecomb.config.client.ConfigurationAction.SET; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.configuration.Configuration; -import org.apache.servicecomb.config.ConfigMapping; -import org.apache.servicecomb.config.client.ApolloClient; -import org.apache.servicecomb.config.client.ApolloConfig; -import org.apache.servicecomb.config.client.ConfigurationAction; -import org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.ImmutableMap; -import com.netflix.config.WatchedUpdateListener; -import com.netflix.config.WatchedUpdateResult; - -public class ApolloConfigurationSourceImpl implements ConfigCenterConfigurationSource { - private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCenterConfigurationSource.class); - - private final Map valueCache = new ConcurrentHashMap<>(); - - private final List listeners = new CopyOnWriteArrayList<>(); - - private static final String APOLLO_CONFIG_URL_KEY = "apollo.config.serverUri"; - - public ApolloConfigurationSourceImpl() { - } - - private final UpdateHandler updateHandler = new UpdateHandler(); - - @VisibleForTesting - UpdateHandler getUpdateHandler() { - return updateHandler; - } - - @Override - public int getOrder() { - return ORDER_BASE * 3; - } - - @Override - public boolean isValidSource(Configuration localConfiguration) { - if (localConfiguration.getProperty(APOLLO_CONFIG_URL_KEY) == null) { - LOGGER.warn("Apollo configuration source is not configured!"); - return false; - } - return true; - } - - @Override - public void init(Configuration localConfiguration) { - ApolloConfig.setConcurrentCompositeConfiguration(localConfiguration); - init(); - } - - private void init() { - ApolloClient apolloClient = new ApolloClient(updateHandler); - apolloClient.refreshApolloConfig(); - } - - @Override - public void addUpdateListener(WatchedUpdateListener watchedUpdateListener) { - listeners.add(watchedUpdateListener); - } - - @Override - public void removeUpdateListener(WatchedUpdateListener watchedUpdateListener) { - listeners.remove(watchedUpdateListener); - } - - private void updateConfiguration(WatchedUpdateResult result) { - for (WatchedUpdateListener l : listeners) { - try { - l.updateConfiguration(result); - } catch (Throwable ex) { - LOGGER.error("Error in invoking WatchedUpdateListener", ex); - } - } - } - - @Override - public Map getCurrentData() throws Exception { - return valueCache; - } - - public List getCurrentListeners() { - return listeners; - } - - public class UpdateHandler { - public void handle(ConfigurationAction action, Map config) { - if (config == null || config.isEmpty()) { - return; - } - Map configuration = ConfigMapping.getConvertedMap(config); - if (CREATE.equals(action)) { - valueCache.putAll(configuration); - - updateConfiguration(createIncremental(ImmutableMap.copyOf(configuration), - null, - null)); - } else if (SET.equals(action)) { - valueCache.putAll(configuration); - - updateConfiguration(createIncremental(null, - ImmutableMap.copyOf(configuration), - null)); - } else if (DELETE.equals(action)) { - configuration.keySet().forEach(valueCache::remove); - updateConfiguration(createIncremental(null, - null, - ImmutableMap.copyOf(configuration))); - } else { - LOGGER.error("action: {} is invalid.", action.name()); - return; - } - LOGGER.warn("Config value cache changed: action:{}; item:{}", action.name(), configuration.keySet()); - } - } -} diff --git a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/client/ApolloConfig.java b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/client/ApolloConfig.java deleted file mode 100644 index b8de462e3c6..00000000000 --- a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/client/ApolloConfig.java +++ /dev/null @@ -1,89 +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.servicecomb.config.client; - -import org.apache.commons.configuration.Configuration; - -public class ApolloConfig { - public static final ApolloConfig INSTANCE = new ApolloConfig(); - - private static Configuration finalConfig; - - private static final String SERVER_URL_KEY = "apollo.config.serverUri"; - - private static final String SERVER_NAMESPACE = "apollo.config.namespace"; - - private static final String SERVER_ENV = "apollo.config.env"; - - private static final String SERVER_CLUSTERS = "apollo.config.clusters"; - - private static final String APOLLO_SERVICE_NAME = "apollo.config.serviceName"; - - private static final String TOKEN = "apollo.config.token"; - - private static final String REFRESH_INTERVAL = "apollo.config.refreshInterval"; - - private static final String FIRST_REFRESH_INTERVAL = "apollo.config.firstRefreshInterval"; - - private static final int DEFAULT_REFRESH_INTERVAL = 3; - - private static final int DEFAULT_FIRST_REFRESH_INTERVAL = 0; - - private ApolloConfig() { - } - - public static void setConcurrentCompositeConfiguration(Configuration config) { - finalConfig = config; - } - - public Configuration getConcurrentCompositeConfiguration() { - return finalConfig; - } - - public String getServiceName() { - return finalConfig.getString(APOLLO_SERVICE_NAME); - } - - public String getServerUri() { - return finalConfig.getString(SERVER_URL_KEY); - } - - public String getToken() { - return finalConfig.getString(TOKEN); - } - - public String getEnv() { - return finalConfig.getString(SERVER_ENV); - } - - public String getNamespace() { - return finalConfig.getString(SERVER_NAMESPACE); - } - - public String getServerClusters() { - return finalConfig.getString(SERVER_CLUSTERS); - } - - public int getRefreshInterval() { - return finalConfig.getInt(REFRESH_INTERVAL, DEFAULT_REFRESH_INTERVAL); - } - - public int getFirstRefreshInterval() { - return finalConfig.getInt(FIRST_REFRESH_INTERVAL, DEFAULT_FIRST_REFRESH_INTERVAL); - } -} diff --git a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/client/ConfigurationAction.java b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/client/ConfigurationAction.java deleted file mode 100644 index 5daf1972cfd..00000000000 --- a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/client/ConfigurationAction.java +++ /dev/null @@ -1,24 +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.servicecomb.config.client; - -public enum ConfigurationAction { - CREATE, - SET, - DELETE -} diff --git a/dynamic-config/config-apollo/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource b/dynamic-config/config-apollo/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource new file mode 100644 index 00000000000..aa0aae8be0b --- /dev/null +++ b/dynamic-config/config-apollo/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource @@ -0,0 +1,18 @@ +# +# 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.servicecomb.config.apollo.ApolloDynamicPropertiesSource diff --git a/dynamic-config/config-apollo/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource b/dynamic-config/config-apollo/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource deleted file mode 100644 index fa6f9b75892..00000000000 --- a/dynamic-config/config-apollo/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.config.archaius.sources.ApolloConfigurationSourceImpl diff --git a/dynamic-config/config-apollo/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/dynamic-config/config-apollo/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..31e45053446 --- /dev/null +++ b/dynamic-config/config-apollo/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.config.apollo.ApolloConfiguration diff --git a/dynamic-config/config-apollo/src/test/java/org/apache/servicecomb/config/apollo/ApolloClientTest.java b/dynamic-config/config-apollo/src/test/java/org/apache/servicecomb/config/apollo/ApolloClientTest.java new file mode 100644 index 00000000000..65bc6632c72 --- /dev/null +++ b/dynamic-config/config-apollo/src/test/java/org/apache/servicecomb/config/apollo/ApolloClientTest.java @@ -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. + */ + +package org.apache.servicecomb.config.apollo; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.config.apollo.ApolloClient.ConfigRefresh; +import org.apache.servicecomb.config.apollo.ApolloDynamicPropertiesSource.UpdateHandler; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +public class ApolloClientTest { + @BeforeAll + public static void setUpClass() { + + } + + @Test + public void refreshApolloConfig() { + ApolloConfig apolloConfig = Mockito.mock(ApolloConfig.class); + RestTemplate rest = Mockito.mock(RestTemplate.class); + ApolloClient.setRest(rest); + + ResponseEntity responseEntity = new ResponseEntity<>( + "{\"apollo\":\"mocked\", \"configurations\":{\"timeout\":1000}}", HttpStatus.OK); + Mockito.when(rest.exchange( + ArgumentMatchers.anyString(), + ArgumentMatchers.any(HttpMethod.class), + ArgumentMatchers.>any(), + ArgumentMatchers.>any())).thenReturn(responseEntity); + ApolloDynamicPropertiesSource impl = new ApolloDynamicPropertiesSource(); + UpdateHandler updateHandler = impl.new UpdateHandler(); + ApolloClient apolloClient = new ApolloClient(updateHandler, apolloConfig); + ConfigRefresh cr = apolloClient.new ConfigRefresh(apolloConfig.getServerUri()); + cr.run(); + + Assertions.assertEquals(1, ApolloClient.getOriginalConfigMap().size()); + } + + @Test + public void testCompareChangedConfig() { + ApolloConfig apolloConfig = Mockito.mock(ApolloConfig.class); + boolean status = true; + Map before = new HashMap<>(); + Map after = new HashMap<>(); + + ApolloDynamicPropertiesSource impl = new ApolloDynamicPropertiesSource(); + UpdateHandler updateHandler = impl.new UpdateHandler(); + ApolloClient apolloClient = new ApolloClient(updateHandler, apolloConfig); + + ConfigRefresh cr = apolloClient.new ConfigRefresh(""); + + try { + cr.compareChangedConfig(before, after); + } catch (Exception e) { + status = false; + } + Assertions.assertTrue(status); + + before.put("test", "testValue"); + try { + cr.compareChangedConfig(before, after); + } catch (Exception e) { + status = false; + } + Assertions.assertTrue(status); + + after.put("test", "testValue2"); + try { + cr.compareChangedConfig(before, after); + } catch (Exception e) { + status = false; + } + Assertions.assertTrue(status); + + try { + cr.compareChangedConfig(before, after); + } catch (Exception e) { + status = false; + } + Assertions.assertTrue(status); + } +} diff --git a/dynamic-config/config-apollo/src/test/java/org/apache/servicecomb/config/archaius/sources/ApolloConfigurationSourceImplTest.java b/dynamic-config/config-apollo/src/test/java/org/apache/servicecomb/config/archaius/sources/ApolloConfigurationSourceImplTest.java deleted file mode 100644 index b01640c9b6c..00000000000 --- a/dynamic-config/config-apollo/src/test/java/org/apache/servicecomb/config/archaius/sources/ApolloConfigurationSourceImplTest.java +++ /dev/null @@ -1,78 +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.servicecomb.config.archaius.sources; - -import static org.apache.servicecomb.config.client.ConfigurationAction.CREATE; -import static org.apache.servicecomb.config.client.ConfigurationAction.DELETE; -import static org.apache.servicecomb.config.client.ConfigurationAction.SET; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.servicecomb.config.archaius.sources.ApolloConfigurationSourceImpl.UpdateHandler; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import com.netflix.config.WatchedUpdateListener; - -public class ApolloConfigurationSourceImplTest { - @Test - public void testCreate() throws Exception { - - ApolloConfigurationSourceImpl apolloConfigurationSource = new ApolloConfigurationSourceImpl(); - apolloConfigurationSource.addUpdateListener(result -> Assertions.assertFalse(result.getAdded().isEmpty())); - UpdateHandler updateHandler = apolloConfigurationSource.getUpdateHandler(); - Map createItems = new HashMap<>(); - createItems.put("testKey", "testValue"); - updateHandler.handle(CREATE, createItems); - } - - @Test - public void testUpdate() throws Exception { - - ApolloConfigurationSourceImpl apolloConfigurationSource = new ApolloConfigurationSourceImpl(); - apolloConfigurationSource.addUpdateListener(result -> Assertions.assertFalse(result.getChanged().isEmpty())); - UpdateHandler updateHandler = apolloConfigurationSource.getUpdateHandler(); - Map updateItems = new HashMap<>(); - updateItems.put("testKey", "testValue"); - updateHandler.handle(SET, updateItems); - } - - @Test - public void testDelete() throws Exception { - ApolloConfigurationSourceImpl apolloConfigurationSource = new ApolloConfigurationSourceImpl(); - apolloConfigurationSource.addUpdateListener(result -> Assertions.assertFalse(result.getDeleted().isEmpty())); - UpdateHandler updateHandler = apolloConfigurationSource.getUpdateHandler(); - Map deleteItems = new HashMap<>(); - deleteItems.put("testKey", "testValue"); - - apolloConfigurationSource.getCurrentData().put("testKey", "testValue"); - updateHandler.handle(DELETE, deleteItems); - Assertions.assertTrue(apolloConfigurationSource.getCurrentData().isEmpty()); - } - - @Test - public void testRemoveUpdateListener() { - ApolloConfigurationSourceImpl apolloConfigurationSource = new ApolloConfigurationSourceImpl(); - WatchedUpdateListener watchedUpdateListener = Mockito.mock(WatchedUpdateListener.class); - apolloConfigurationSource.addUpdateListener(watchedUpdateListener); - apolloConfigurationSource.removeUpdateListener(watchedUpdateListener); - Assertions.assertTrue(apolloConfigurationSource.getCurrentListeners().isEmpty()); - } -} diff --git a/dynamic-config/config-apollo/src/test/java/org/apache/servicecomb/config/client/ApolloClientTest.java b/dynamic-config/config-apollo/src/test/java/org/apache/servicecomb/config/client/ApolloClientTest.java deleted file mode 100644 index 66ae6c18fb4..00000000000 --- a/dynamic-config/config-apollo/src/test/java/org/apache/servicecomb/config/client/ApolloClientTest.java +++ /dev/null @@ -1,109 +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.servicecomb.config.client; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.config.archaius.sources.ApolloConfigurationSourceImpl; -import org.apache.servicecomb.config.archaius.sources.ApolloConfigurationSourceImpl.UpdateHandler; -import org.apache.servicecomb.config.client.ApolloClient.ConfigRefresh; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentMatchers; -import org.mockito.Mockito; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; - -public class ApolloClientTest { - @BeforeAll - public static void setUpClass() { - - ApolloConfig.setConcurrentCompositeConfiguration(ConfigUtil.createLocalConfig()); - } - - @Test - public void refreshApolloConfig() { - ApolloConfig apolloConfig = ApolloConfig.INSTANCE; - RestTemplate rest = Mockito.mock(RestTemplate.class); - ApolloClient.setRest(rest); - - ResponseEntity responseEntity = new ResponseEntity<>( - "{\"apollo\":\"mocked\", \"configurations\":{\"timeout\":1000}}", HttpStatus.OK); - Mockito.when(rest.exchange( - ArgumentMatchers.anyString(), - ArgumentMatchers.any(HttpMethod.class), - ArgumentMatchers.>any(), - ArgumentMatchers.>any())).thenReturn(responseEntity); - ApolloConfigurationSourceImpl impl = new ApolloConfigurationSourceImpl(); - UpdateHandler updateHandler = impl.new UpdateHandler(); - ApolloClient apolloClient = new ApolloClient(updateHandler); - ConfigRefresh cr = apolloClient.new ConfigRefresh(apolloConfig.getServerUri()); - cr.run(); - - Assertions.assertEquals(1, ApolloClient.getOriginalConfigMap().size()); - } - - @Test - public void testCompareChangedConfig() { - boolean status = true; - Map before = new HashMap<>(); - Map after = new HashMap<>(); - - ApolloConfigurationSourceImpl impl = new ApolloConfigurationSourceImpl(); - UpdateHandler updateHandler = impl.new UpdateHandler(); - ApolloClient apolloClient = new ApolloClient(updateHandler); - - ConfigRefresh cr = apolloClient.new ConfigRefresh(""); - - try { - cr.compareChangedConfig(before, after); - } catch (Exception e) { - status = false; - } - Assertions.assertTrue(status); - - before.put("test", "testValue"); - try { - cr.compareChangedConfig(before, after); - } catch (Exception e) { - status = false; - } - Assertions.assertTrue(status); - - after.put("test", "testValue2"); - try { - cr.compareChangedConfig(before, after); - } catch (Exception e) { - status = false; - } - Assertions.assertTrue(status); - - try { - cr.compareChangedConfig(before, after); - } catch (Exception e) { - status = false; - } - Assertions.assertTrue(status); - } -} diff --git a/dynamic-config/config-apollo/src/test/java/org/apache/servicecomb/config/client/ApolloConfigTest.java b/dynamic-config/config-apollo/src/test/java/org/apache/servicecomb/config/client/ApolloConfigTest.java deleted file mode 100644 index dfcdbbeb921..00000000000 --- a/dynamic-config/config-apollo/src/test/java/org/apache/servicecomb/config/client/ApolloConfigTest.java +++ /dev/null @@ -1,43 +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.servicecomb.config.client; - -import org.apache.servicecomb.config.ConfigUtil; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class ApolloConfigTest { - @BeforeAll - public static void setUpClass() { - ApolloConfig.setConcurrentCompositeConfiguration(ConfigUtil.createLocalConfig()); - } - - @Test - public void getServiceName() { - ApolloConfig instance = ApolloConfig.INSTANCE; - Assertions.assertEquals("apollo-test", instance.getServiceName()); - Assertions.assertEquals("http://127.0.0.1:8070", instance.getServerUri()); - Assertions.assertEquals("DEV", instance.getEnv()); - Assertions.assertEquals("test-cluster", instance.getServerClusters()); - Assertions.assertEquals("application", instance.getNamespace()); - Assertions.assertEquals("xxx", instance.getToken()); - Assertions.assertEquals(30, instance.getRefreshInterval()); - Assertions.assertEquals(0, instance.getFirstRefreshInterval()); - } -} diff --git a/dynamic-config/config-cc/pom.xml b/dynamic-config/config-cc/pom.xml index a841532d71c..0e8f6addcc6 100644 --- a/dynamic-config/config-cc/pom.xml +++ b/dynamic-config/config-cc/pom.xml @@ -21,7 +21,7 @@ dynamic-config org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -33,10 +33,6 @@ org.apache.servicecomb foundation-config - - org.apache.servicecomb - deployment - org.apache.servicecomb foundation-ssl diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/ConfigCenterConfig.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/ConfigCenterConfig.java deleted file mode 100644 index 837f6e0c720..00000000000 --- a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/ConfigCenterConfig.java +++ /dev/null @@ -1,138 +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.servicecomb.config; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.apache.servicecomb.config.collect.ConfigCenterDefaultDeploymentProvider; -import org.apache.servicecomb.deployment.Deployment; -import org.apache.servicecomb.foundation.vertx.VertxConst; - -import com.netflix.config.ConcurrentCompositeConfiguration; - -public final class ConfigCenterConfig { - public static final ConfigCenterConfig INSTANCE = new ConfigCenterConfig(); - - public static final String SSL_TAG = "cc.consumer"; - - private static ConcurrentCompositeConfiguration finalConfig; - - private static final String AUTO_DISCOVERY_ENABLED = "servicecomb.service.registry.autodiscovery"; - - private static final String DOMAIN_NAME = "servicecomb.config.client.domainName"; - - private static final String REFRESH_INTERVAL = "servicecomb.config.client.refresh_interval"; - - private static final String FIRST_REFRESH_INTERVAL = "servicecomb.config.client.first_refresh_interval"; - - private static final String FIRST_PULL_REQUIRED = "servicecomb.config.client.firstPullRequired"; - - public static final String FILE_SOURCE = "servicecomb.config.client.fileSource"; - - private static final int DEFAULT_REFRESH_INTERVAL = 15000; - - private static final int DEFAULT_FIRST_REFRESH_INTERVAL = 0; - - private ConfigCenterConfig() { - } - - public static void setConcurrentCompositeConfiguration(ConcurrentCompositeConfiguration config) { - finalConfig = config; - } - - public static ConcurrentCompositeConfiguration getConcurrentCompositeConfiguration() { - return finalConfig; - } - - - public String getDomainName() { - return finalConfig.getString(DOMAIN_NAME, "default"); - } - - public boolean firstPullRequired() { - return finalConfig.getBoolean(FIRST_PULL_REQUIRED, false); - } - - @SuppressWarnings("unchecked") - public List getFileSources() { - Object property = finalConfig.getProperty(FILE_SOURCE); - if (property instanceof String) { - return new ArrayList<>(Arrays.asList(((String) property).split(","))); - } - if (property instanceof List) { - return (List) property; - } - return Collections.EMPTY_LIST; - } - - public long getRefreshInterval() { - return finalConfig.getLong(REFRESH_INTERVAL, DEFAULT_REFRESH_INTERVAL); - } - - public int getFirstRefreshInterval() { - return finalConfig.getInt(FIRST_REFRESH_INTERVAL, DEFAULT_FIRST_REFRESH_INTERVAL); - } - - public Boolean isProxyEnable() { - return finalConfig.getBoolean(VertxConst.PROXY_ENABLE, false); - } - - public String getProxyHost() { - return finalConfig.getString(VertxConst.PROXY_HOST, "127.0.0.1"); - } - - public int getProxyPort() { - return finalConfig.getInt(VertxConst.PROXY_PORT, 8080); - } - - public String getProxyUsername() { - return finalConfig.getString(VertxConst.PROXY_USERNAME, null); - } - - public String getProxyPasswd() { - return finalConfig.getString(VertxConst.PROXY_PASSWD, null); - } - - public String getServiceName() { - return BootStrapProperties.readServiceName(finalConfig); - } - - public String getAppName() { - return BootStrapProperties.readApplication(finalConfig); - } - - public String getServiceVersion() { - return BootStrapProperties.readServiceVersion(finalConfig); - } - - public List getServerUri() { - return Deployment.getSystemBootStrapInfo(ConfigCenterDefaultDeploymentProvider.SYSTEM_KEY_CONFIG_CENTER) - .getAccessURL(); - } - - public boolean getAutoDiscoveryEnabled() { - return finalConfig.getBoolean(AUTO_DISCOVERY_ENABLED, false); - } - - public String getEnvironment() { - return BootStrapProperties.readServiceEnvironment(finalConfig); - } -} diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/ConfigCenterConfigurationSourceImpl.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/ConfigCenterConfigurationSourceImpl.java deleted file mode 100644 index 74834aaef6d..00000000000 --- a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/ConfigCenterConfigurationSourceImpl.java +++ /dev/null @@ -1,231 +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.servicecomb.config; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.apache.commons.configuration.Configuration; -import org.apache.http.HttpHost; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.servicecomb.config.center.client.ConfigCenterAddressManager; -import org.apache.servicecomb.config.center.client.ConfigCenterClient; -import org.apache.servicecomb.config.center.client.model.ConfigCenterConfiguration; -import org.apache.servicecomb.config.center.client.ConfigCenterManager; -import org.apache.servicecomb.config.center.client.model.QueryConfigurationsRequest; -import org.apache.servicecomb.config.center.client.model.QueryConfigurationsResponse; -import org.apache.servicecomb.config.collect.ConfigCenterDefaultDeploymentProvider; -import org.apache.servicecomb.config.common.ConfigConverter; -import org.apache.servicecomb.config.common.ConfigurationChangedEvent; -import org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource; -import org.apache.servicecomb.deployment.Deployment; -import org.apache.servicecomb.deployment.SystemBootstrapInfo; -import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; -import org.apache.servicecomb.foundation.common.event.EventManager; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; -import org.apache.servicecomb.http.client.common.HttpTransport; -import org.apache.servicecomb.http.client.common.HttpTransportFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.eventbus.Subscribe; -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.WatchedUpdateListener; -import com.netflix.config.WatchedUpdateResult; - -public class ConfigCenterConfigurationSourceImpl implements ConfigCenterConfigurationSource { - private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCenterConfigurationSourceImpl.class); - - private final List listeners = new CopyOnWriteArrayList<>(); - - private ConfigCenterManager configCenterManager; - - private ConfigConverter configConverter; - - @Override - public int getOrder() { - return ORDER_BASE * 2; - } - - @Override - public boolean isValidSource(Configuration localConfiguration) { - ConfigCenterConfig.setConcurrentCompositeConfiguration((ConcurrentCompositeConfiguration) localConfiguration); - SystemBootstrapInfo address = Deployment - .getSystemBootStrapInfo(ConfigCenterDefaultDeploymentProvider.SYSTEM_KEY_CONFIG_CENTER); - - if (address == null) { - LOGGER.info("config center server is not configured."); - return false; - } - return true; - } - - @Override - public void init(Configuration localConfiguration) { - configConverter = new ConfigConverter(ConfigCenterConfig.INSTANCE.getFileSources()); - - ConfigCenterAddressManager kieAddressManager = configKieAddressManager(); - - HttpTransport httpTransport = createHttpTransport(kieAddressManager, - HttpTransportFactory.defaultRequestConfig().build(), - localConfiguration); - ConfigCenterClient configCenterClient = new ConfigCenterClient(kieAddressManager, httpTransport); - EventManager.register(this); - - ConfigCenterConfiguration configCenterConfiguration = createConfigCenterConfiguration(); - - QueryConfigurationsRequest queryConfigurationsRequest = firstPull(configCenterClient); - - configCenterManager = new ConfigCenterManager(configCenterClient, EventManager.getEventBus(), - configConverter, configCenterConfiguration); - configCenterManager.setQueryConfigurationsRequest(queryConfigurationsRequest); - configCenterManager.startConfigCenterManager(); - } - - private QueryConfigurationsRequest firstPull(ConfigCenterClient configCenterClient) { - QueryConfigurationsRequest queryConfigurationsRequest = createQueryConfigurationsRequest(); - try { - QueryConfigurationsResponse response = configCenterClient - .queryConfigurations(queryConfigurationsRequest); - if (response.isChanged()) { - configConverter.updateData(response.getConfigurations()); - updateConfiguration(WatchedUpdateResult.createIncremental(configConverter.getCurrentData(), null, null)); - queryConfigurationsRequest.setRevision(response.getRevision()); - } - } catch (Exception e) { - if (ConfigCenterConfig.INSTANCE.firstPullRequired()) { - throw e; - } - LOGGER.warn("first pull failed, and ignore {}", e.getMessage()); - } - return queryConfigurationsRequest; - } - - @Subscribe - public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { - updateConfiguration( - WatchedUpdateResult.createIncremental(event.getAdded(), event.getUpdated(), event.getDeleted())); - } - - private QueryConfigurationsRequest createQueryConfigurationsRequest() { - QueryConfigurationsRequest request = new QueryConfigurationsRequest(); - request.setApplication(ConfigCenterConfig.INSTANCE.getAppName()); - request.setServiceName(ConfigCenterConfig.INSTANCE.getServiceName()); - request.setVersion(ConfigCenterConfig.INSTANCE.getServiceVersion()); - request.setEnvironment(ConfigCenterConfig.INSTANCE.getEnvironment()); - // 需要设置为 null, 并且 query 参数为 revision=null 才会返回 revision 信息。 revision = 是不行的。 - request.setRevision(null); - return request; - } - - private ConfigCenterConfiguration createConfigCenterConfiguration(){ - return new ConfigCenterConfiguration().setRefreshIntervalInMillis(ConfigCenterConfig.INSTANCE.getRefreshInterval()); - } - - private HttpTransport createHttpTransport(ConfigCenterAddressManager kieAddressManager, RequestConfig requestConfig, - Configuration localConfiguration) { - List authHeaderProviders = SPIServiceUtils.getOrLoadSortedService(AuthHeaderProvider.class); - - if (ConfigCenterConfig.INSTANCE.isProxyEnable()) { - HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). - setDefaultRequestConfig(requestConfig); - HttpHost proxy = new HttpHost(ConfigCenterConfig.INSTANCE.getProxyHost(), - ConfigCenterConfig.INSTANCE.getProxyPort(), "http"); // now only support http proxy - httpClientBuilder.setProxy(proxy); - CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(new AuthScope(proxy), - new UsernamePasswordCredentials(ConfigCenterConfig.INSTANCE.getProxyUsername(), - ConfigCenterConfig.INSTANCE.getProxyPasswd())); - httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); - - return HttpTransportFactory - .createHttpTransport( - TransportUtils - .createSSLProperties(kieAddressManager.sslEnabled(), localConfiguration, ConfigCenterConfig.SSL_TAG), - getRequestAuthHeaderProvider(authHeaderProviders), httpClientBuilder); - } - - return HttpTransportFactory - .createHttpTransport( - TransportUtils - .createSSLProperties(kieAddressManager.sslEnabled(), localConfiguration, ConfigCenterConfig.SSL_TAG), - getRequestAuthHeaderProvider(authHeaderProviders), requestConfig); - } - - private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List authHeaderProviders) { - return signRequest -> { - Map headers = new HashMap<>(); - authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders())); - return headers; - }; - } - - private ConfigCenterAddressManager configKieAddressManager() { - return new ConfigCenterAddressManager(ConfigCenterConfig.INSTANCE.getDomainName(), - Deployment - .getSystemBootStrapInfo(ConfigCenterDefaultDeploymentProvider.SYSTEM_KEY_CONFIG_CENTER).getAccessURL(), - EventManager.getEventBus()); - } - - private void updateConfiguration(WatchedUpdateResult result) { - LOGGER.info("configuration changed keys, added=[{}], updated=[{}], deleted=[{}]", - result.getAdded() == null ? "" : result.getAdded().keySet(), - result.getChanged() == null ? "" : result.getChanged().keySet(), - result.getDeleted() == null ? "" : result.getDeleted().keySet()); - for (WatchedUpdateListener l : listeners) { - try { - l.updateConfiguration(result); - } catch (Throwable ex) { - LOGGER.error("Error in invoking WatchedUpdateListener", ex); - } - } - } - - @Override - public void destroy() { - if (configCenterManager == null) { - return; - } - configCenterManager.stop(); - } - - @Override - public void addUpdateListener(WatchedUpdateListener watchedUpdateListener) { - listeners.add(watchedUpdateListener); - } - - @Override - public void removeUpdateListener(WatchedUpdateListener watchedUpdateListener) { - listeners.remove(watchedUpdateListener); - } - - @Override - public Map getCurrentData() throws Exception { - // data will updated by first pull, set empty to DynamicWatchedConfiguration first. - return Collections.emptyMap(); - } -} diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/TransportUtils.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/TransportUtils.java deleted file mode 100644 index 21a510ac413..00000000000 --- a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/TransportUtils.java +++ /dev/null @@ -1,128 +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.servicecomb.config; - -import static org.apache.servicecomb.foundation.ssl.SSLOption.DEFAULT_OPTION; - -import org.apache.commons.configuration.Configuration; -import org.apache.servicecomb.foundation.ssl.SSLCustom; -import org.apache.servicecomb.foundation.ssl.SSLOption; -import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; - -public class TransportUtils { - public static SSLProperties createSSLProperties(boolean sslEnabled, Configuration configuration, String tag) { - SSLProperties sslProperties = new SSLProperties(); - sslProperties.setEnabled(sslEnabled); - - if (!sslEnabled) { - return sslProperties; - } - - SSLOption option = new SSLOption(); - option.setEngine(getStringProperty(configuration, - DEFAULT_OPTION.getEngine(), - "ssl." + tag + ".engine", - "ssl.engine")); - option.setProtocols( - getStringProperty(configuration, - DEFAULT_OPTION.getProtocols(), - "ssl." + tag + ".protocols", - "ssl.protocols")); - option.setCiphers( - getStringProperty(configuration, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers")); - option.setAuthPeer( - getBooleanProperty(configuration, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer")); - option.setCheckCNHost( - getBooleanProperty(configuration, - DEFAULT_OPTION.isCheckCNHost(), - "ssl." + tag + ".checkCN.host", - "ssl.checkCN.host")); - option.setCheckCNWhite( - getBooleanProperty(configuration, - DEFAULT_OPTION.isCheckCNWhite(), - "ssl." + tag + ".checkCN.white", - "ssl.checkCN.white")); - option.setCheckCNWhiteFile(getStringProperty(configuration, - DEFAULT_OPTION.getCiphers(), - "ssl." + tag + ".checkCN.white.file", - "ssl.checkCN.white.file")); - option.setAllowRenegociate(getBooleanProperty(configuration, - DEFAULT_OPTION.isAllowRenegociate(), - "ssl." + tag + ".allowRenegociate", - "ssl.allowRenegociate")); - option.setStorePath( - getStringProperty(configuration, - DEFAULT_OPTION.getStorePath(), - "ssl." + tag + ".storePath", - "ssl.storePath")); - option.setClientAuth( - getStringProperty(configuration, - DEFAULT_OPTION.getClientAuth(), - "ssl." + tag + ".clientAuth", - "ssl.clientAuth")); - option.setTrustStore( - getStringProperty(configuration, - DEFAULT_OPTION.getTrustStore(), - "ssl." + tag + ".trustStore", - "ssl.trustStore")); - option.setTrustStoreType(getStringProperty(configuration, - DEFAULT_OPTION.getTrustStoreType(), - "ssl." + tag + ".trustStoreType", - "ssl.trustStoreType")); - option.setTrustStoreValue(getStringProperty(configuration, - DEFAULT_OPTION.getTrustStoreValue(), - "ssl." + tag + ".trustStoreValue", - "ssl.trustStoreValue")); - option.setKeyStore( - getStringProperty(configuration, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore")); - option.setKeyStoreType( - getStringProperty(configuration, - DEFAULT_OPTION.getKeyStoreType(), - "ssl." + tag + ".keyStoreType", - "ssl.keyStoreType")); - option.setKeyStoreValue(getStringProperty(configuration, - DEFAULT_OPTION.getKeyStoreValue(), - "ssl." + tag + ".keyStoreValue", - "ssl.keyStoreValue")); - option.setCrl(getStringProperty(configuration, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl")); - option.setSslCustomClass( - getStringProperty(configuration, null, "ssl." + tag + ".sslCustomClass", "ssl.sslCustomClass")); - - sslProperties.setSslOption(option); - sslProperties.setSslCustom(SSLCustom.createSSLCustom(option.getSslCustomClass())); - return sslProperties; - } - - private static String getStringProperty(Configuration configuration, String defaultValue, String... keys) { - for (String key : keys) { - if (configuration.containsKey(key)) { - return configuration.getString(key); - } - } - return defaultValue; - } - - private static boolean getBooleanProperty(Configuration configuration, boolean defaultValue, String... keys) { - for (String key : keys) { - if (configuration.containsKey(key)) { - return configuration.getBoolean(key); - } - } - return defaultValue; - } -} diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterConfig.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterConfig.java new file mode 100644 index 00000000000..c1cf7c1b683 --- /dev/null +++ b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterConfig.java @@ -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. + */ + +package org.apache.servicecomb.config.cc; + +import java.util.Collections; +import java.util.List; + +import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.foundation.vertx.VertxConst; +import org.springframework.core.env.Environment; + +public final class ConfigCenterConfig { + public static final String SSL_TAG = "cc.consumer"; + + private static final String ADDRESS = "servicecomb.config.client.serverUri"; + + private static final String DOMAIN_NAME = "servicecomb.config.client.domainName"; + + private static final String REFRESH_INTERVAL = "servicecomb.config.client.refresh_interval"; + + private static final String FIRST_PULL_REQUIRED = "servicecomb.config.client.firstPullRequired"; + + public static final String FILE_SOURCE = "servicecomb.config.client.fileSource"; + + private static final int DEFAULT_REFRESH_INTERVAL = 15000; + + private static final String CLIENT_CONNECT_TIMEOUT = "servicecomb.config.client.timeout.connect"; + + private static final String CLIENT_REQUEST_TIMEOUT = "servicecomb.config.client.timeout.request"; + + private static final String CLIENT_SOCKET_TIMEOUT = "servicecomb.config.client.timeout.socket"; + + private final Environment environment; + + public ConfigCenterConfig(Environment environment) { + this.environment = environment; + } + + public String getDomainName() { + return environment.getProperty(DOMAIN_NAME, "default"); + } + + public boolean firstPullRequired() { + return environment.getProperty(FIRST_PULL_REQUIRED, boolean.class, false); + } + + @SuppressWarnings("unchecked") + public List getFileSources() { + return environment.getProperty(FILE_SOURCE, List.class, Collections.emptyList()); + } + + public long getRefreshInterval() { + return environment.getProperty( + REFRESH_INTERVAL, long.class, (long) DEFAULT_REFRESH_INTERVAL); + } + + public Boolean isProxyEnable() { + return environment.getProperty(VertxConst.PROXY_ENABLE, boolean.class, false); + } + + public String getProxyHost() { + return environment.getProperty(VertxConst.PROXY_HOST, "127.0.0.1"); + } + + public int getProxyPort() { + return environment.getProperty(VertxConst.PROXY_PORT, int.class, 8080); + } + + public String getProxyUsername() { + return environment.getProperty(VertxConst.PROXY_USERNAME); + } + + public String getProxyPasswd() { + return environment.getProperty(VertxConst.PROXY_PASSWD); + } + + public List getServerUri() { + return ConfigUtil.parseArrayValue(environment.getProperty(ADDRESS, "")); + } + + public int getConnectTimeout() { + return environment.getProperty(CLIENT_CONNECT_TIMEOUT, int.class, 5000); + } + + public int getConnectionRequestTimeout() { + return environment.getProperty(CLIENT_REQUEST_TIMEOUT, int.class, 5000); + } + + public int getSocketTimeout() { + return environment.getProperty(CLIENT_SOCKET_TIMEOUT, int.class, 5000); + } +} diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterConfiguration.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterConfiguration.java new file mode 100644 index 00000000000..672a5931faf --- /dev/null +++ b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterConfiguration.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.servicecomb.config.cc; + +import org.springframework.context.annotation.Bean; + +public class ConfigCenterConfiguration { + @Bean + public ConfigCenterInformationCollector configCenterInformationCollector() { + return new ConfigCenterInformationCollector(); + } +} diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterDynamicPropertiesSource.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterDynamicPropertiesSource.java new file mode 100644 index 00000000000..23c0a7af196 --- /dev/null +++ b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterDynamicPropertiesSource.java @@ -0,0 +1,203 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.cc; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.config.DynamicPropertiesSource; +import org.apache.servicecomb.config.center.client.ConfigCenterAddressManager; +import org.apache.servicecomb.config.center.client.ConfigCenterClient; +import org.apache.servicecomb.config.center.client.ConfigCenterConfigurationChangedEvent; +import org.apache.servicecomb.config.center.client.ConfigCenterManager; +import org.apache.servicecomb.config.center.client.model.ConfigCenterConfiguration; +import org.apache.servicecomb.config.center.client.model.QueryConfigurationsRequest; +import org.apache.servicecomb.config.center.client.model.QueryConfigurationsResponse; +import org.apache.servicecomb.config.common.ConfigConverter; +import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; +import org.apache.servicecomb.http.client.common.HttpTransport; +import org.apache.servicecomb.http.client.common.HttpTransportFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +import com.google.common.eventbus.Subscribe; + +public class ConfigCenterDynamicPropertiesSource implements DynamicPropertiesSource { + public static final String SOURCE_NAME = "config-center"; + + private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCenterDynamicPropertiesSource.class); + + private final Map data = new ConcurrentHashMapEx<>(); + + private ConfigConverter configConverter; + + public ConfigCenterDynamicPropertiesSource() { + } + + private void init(Environment environment) { + ConfigCenterConfig configCenterConfig = new ConfigCenterConfig(environment); + configConverter = new ConfigConverter(configCenterConfig.getFileSources()); + + ConfigCenterAddressManager configCenterAddressManager = configCenterAddressManager(configCenterConfig, environment); + + HttpTransport httpTransport = createHttpTransport(configCenterAddressManager, + buildRequestConfig(configCenterConfig), environment, configCenterConfig); + ConfigCenterClient configCenterClient = new ConfigCenterClient(configCenterAddressManager, httpTransport); + EventManager.register(this); + + ConfigCenterConfiguration configCenterConfiguration = + createConfigCenterConfiguration(configCenterConfig); + + QueryConfigurationsRequest queryConfigurationsRequest = firstPull(configCenterConfig, configCenterClient, + environment, configCenterAddressManager); + + ConfigCenterManager configCenterManager = new ConfigCenterManager(configCenterClient, EventManager.getEventBus(), + configConverter, configCenterConfiguration, configCenterAddressManager); + configCenterManager.setQueryConfigurationsRequest(queryConfigurationsRequest); + configCenterManager.startConfigCenterManager(); + data.putAll(configConverter.getCurrentData()); + } + + private RequestConfig buildRequestConfig(ConfigCenterConfig config) { + RequestConfig.Builder builder = HttpTransportFactory.defaultRequestConfig(); + builder.setConnectTimeout(config.getConnectTimeout()); + builder.setConnectionRequestTimeout(config.getConnectionRequestTimeout()); + builder.setSocketTimeout(config.getSocketTimeout()); + return builder.build(); + } + + private QueryConfigurationsRequest firstPull(ConfigCenterConfig configCenterConfig, + ConfigCenterClient configCenterClient, Environment environment, + ConfigCenterAddressManager configCenterAddressManager) { + QueryConfigurationsRequest queryConfigurationsRequest = createQueryConfigurationsRequest(environment); + try { + QueryConfigurationsResponse response = configCenterClient + .queryConfigurations(queryConfigurationsRequest, configCenterAddressManager.address()); + if (response.isChanged()) { + configConverter.updateData(response.getConfigurations()); + queryConfigurationsRequest.setRevision(response.getRevision()); + } + } catch (Exception e) { + if (configCenterConfig.firstPullRequired()) { + throw e; + } + LOGGER.warn("first pull failed, and ignore {}", e.getMessage()); + } + return queryConfigurationsRequest; + } + + @Subscribe + public void onConfigurationChangedEvent(ConfigCenterConfigurationChangedEvent event) { + LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); + data.putAll(event.getAdded()); + data.putAll(event.getUpdated()); + event.getDeleted().forEach((k, v) -> data.remove(k)); + EventManager.post(ConfigurationChangedEvent.createIncremental(event.getAdded(), + event.getUpdated(), event.getDeleted())); + } + + private QueryConfigurationsRequest createQueryConfigurationsRequest(Environment environment) { + QueryConfigurationsRequest request = new QueryConfigurationsRequest(); + request.setApplication(BootStrapProperties.readApplication(environment)); + request.setServiceName(BootStrapProperties.readServiceName(environment)); + request.setVersion(BootStrapProperties.readServiceVersion(environment)); + request.setEnvironment(BootStrapProperties.readServiceEnvironment(environment)); + // 需要设置为 null, 并且 query 参数为 revision=null 才会返回 revision 信息。 revision = 是不行的。 + request.setRevision(null); + return request; + } + + private ConfigCenterConfiguration createConfigCenterConfiguration( + ConfigCenterConfig configCenterConfig) { + return new ConfigCenterConfiguration().setRefreshIntervalInMillis(configCenterConfig.getRefreshInterval()); + } + + private HttpTransport createHttpTransport(ConfigCenterAddressManager configCenterAddressManager, + RequestConfig requestConfig, + Environment environment, ConfigCenterConfig configCenterConfig) { + List authHeaderProviders = SPIServiceUtils.getOrLoadSortedService(AuthHeaderProvider.class); + + if (configCenterConfig.isProxyEnable()) { + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). + setDefaultRequestConfig(requestConfig); + HttpHost proxy = new HttpHost(configCenterConfig.getProxyHost(), + configCenterConfig.getProxyPort(), "http"); // now only support http proxy + httpClientBuilder.setProxy(proxy); + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(new AuthScope(proxy), + new UsernamePasswordCredentials(configCenterConfig.getProxyUsername(), + configCenterConfig.getProxyPasswd())); + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + + return HttpTransportFactory + .createHttpTransport( + TransportUtils + .createSSLProperties(configCenterAddressManager.sslEnabled(), environment, ConfigCenterConfig.SSL_TAG), + getRequestAuthHeaderProvider(authHeaderProviders), httpClientBuilder); + } + + return HttpTransportFactory + .createHttpTransport( + TransportUtils + .createSSLProperties(configCenterAddressManager.sslEnabled(), environment, ConfigCenterConfig.SSL_TAG), + getRequestAuthHeaderProvider(authHeaderProviders), requestConfig); + } + + private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List authHeaderProviders) { + return signRequest -> { + String host = signRequest != null && signRequest.getEndpoint() != null ? signRequest.getEndpoint().getHost() : ""; + Map headers = new HashMap<>(); + authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders(host))); + return headers; + }; + } + + private ConfigCenterAddressManager configCenterAddressManager(ConfigCenterConfig configCenterConfig, Environment environment) { + String region = environment.getProperty("servicecomb.datacenter.region"); + String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); + return new ConfigCenterAddressManager(configCenterConfig.getDomainName(), + configCenterConfig.getServerUri(), EventManager.getEventBus(), region, availableZone); + } + + @Override + public PropertySource create(Environment environment) { + init(environment); + return new MapPropertySource(SOURCE_NAME, data); + } + + @Override + public int getOrder() { + return 200; + } +} diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterInformationCollector.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterInformationCollector.java new file mode 100644 index 00000000000..ab91f368013 --- /dev/null +++ b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterInformationCollector.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.servicecomb.config.cc; + +import org.apache.servicecomb.core.bootup.BootUpInformationCollector; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; +import org.springframework.lang.NonNull; + + +public class ConfigCenterInformationCollector implements BootUpInformationCollector, EnvironmentAware { + private ConfigCenterConfig configCenterConfig; + + @Override + public void setEnvironment(@NonNull Environment environment) { + this.configCenterConfig = new ConfigCenterConfig(environment); + } + + @Override + public String collect() { + return "Config Center: " + configCenterConfig.getServerUri(); + } + + @Override + public int getOrder() { + return 1; + } +} diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/TransportUtils.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/TransportUtils.java new file mode 100644 index 00000000000..25a4dac0543 --- /dev/null +++ b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/TransportUtils.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config.cc; + +import static org.apache.servicecomb.foundation.ssl.SSLOption.DEFAULT_OPTION; + +import org.apache.servicecomb.foundation.ssl.SSLCustom; +import org.apache.servicecomb.foundation.ssl.SSLOption; +import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; +import org.springframework.core.env.Environment; + +public class TransportUtils { + public static SSLProperties createSSLProperties(boolean sslEnabled, Environment environment, String tag) { + SSLProperties sslProperties = new SSLProperties(); + sslProperties.setEnabled(sslEnabled); + + if (!sslEnabled) { + return sslProperties; + } + + SSLOption option = new SSLOption(); + option.setEngine(getStringProperty(environment, + DEFAULT_OPTION.getEngine(), + "ssl." + tag + ".engine", + "ssl.engine")); + option.setProtocols( + getStringProperty(environment, + DEFAULT_OPTION.getProtocols(), + "ssl." + tag + ".protocols", + "ssl.protocols")); + option.setCiphers( + getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers")); + option.setAuthPeer( + getBooleanProperty(environment, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer")); + option.setCheckCNHost( + getBooleanProperty(environment, + DEFAULT_OPTION.isCheckCNHost(), + "ssl." + tag + ".checkCN.host", + "ssl.checkCN.host")); + option.setCheckCNWhite( + getBooleanProperty(environment, + DEFAULT_OPTION.isCheckCNWhite(), + "ssl." + tag + ".checkCN.white", + "ssl.checkCN.white")); + option.setCheckCNWhiteFile(getStringProperty(environment, + DEFAULT_OPTION.getCiphers(), + "ssl." + tag + ".checkCN.white.file", + "ssl.checkCN.white.file")); + option.setAllowRenegotiate(getBooleanProperty(environment, + DEFAULT_OPTION.isAllowRenegotiate(), + "ssl." + tag + ".allowRenegotiate", + "ssl.allowRenegotiate")); + option.setStorePath( + getStringProperty(environment, + DEFAULT_OPTION.getStorePath(), + "ssl." + tag + ".storePath", + "ssl.storePath")); + option.setClientAuth( + getStringProperty(environment, + DEFAULT_OPTION.getClientAuth(), + "ssl." + tag + ".clientAuth", + "ssl.clientAuth")); + option.setTrustStore( + getStringProperty(environment, + DEFAULT_OPTION.getTrustStore(), + "ssl." + tag + ".trustStore", + "ssl.trustStore")); + option.setTrustStoreType(getStringProperty(environment, + DEFAULT_OPTION.getTrustStoreType(), + "ssl." + tag + ".trustStoreType", + "ssl.trustStoreType")); + option.setTrustStoreValue(getStringProperty(environment, + DEFAULT_OPTION.getTrustStoreValue(), + "ssl." + tag + ".trustStoreValue", + "ssl.trustStoreValue")); + option.setKeyStore( + getStringProperty(environment, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore")); + option.setKeyStoreType( + getStringProperty(environment, + DEFAULT_OPTION.getKeyStoreType(), + "ssl." + tag + ".keyStoreType", + "ssl.keyStoreType")); + option.setKeyStoreValue(getStringProperty(environment, + DEFAULT_OPTION.getKeyStoreValue(), + "ssl." + tag + ".keyStoreValue", + "ssl.keyStoreValue")); + option.setCrl(getStringProperty(environment, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl")); + option.setSslCustomClass( + getStringProperty(environment, null, "ssl." + tag + ".sslCustomClass", "ssl.sslCustomClass")); + + sslProperties.setSslOption(option); + sslProperties.setSslCustom(SSLCustom.createSSLCustom(option.getSslCustomClass())); + return sslProperties; + } + + private static String getStringProperty(Environment environment, String defaultValue, String... keys) { + for (String key : keys) { + if (environment.getProperty(key) != null) { + return environment.getProperty(key); + } + } + return defaultValue; + } + + private static boolean getBooleanProperty(Environment environment, boolean defaultValue, String... keys) { + for (String key : keys) { + if (environment.getProperty(key) != null) { + return environment.getProperty(key, boolean.class, false); + } + } + return defaultValue; + } +} diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/collect/ConfigCenterDefaultDeploymentProvider.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/collect/ConfigCenterDefaultDeploymentProvider.java deleted file mode 100644 index 34cfa73326c..00000000000 --- a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/collect/ConfigCenterDefaultDeploymentProvider.java +++ /dev/null @@ -1,53 +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.servicecomb.config.collect; - -import java.util.List; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.deployment.DeploymentProvider; -import org.apache.servicecomb.deployment.SystemBootstrapInfo; - -import com.google.common.annotations.VisibleForTesting; - -public class ConfigCenterDefaultDeploymentProvider implements DeploymentProvider { - public static final String SYSTEM_KEY_CONFIG_CENTER = "ConfigCenter"; - - private static AbstractConfiguration configuration = ConfigUtil.createLocalConfig(); - - @Override - public SystemBootstrapInfo getSystemBootStrapInfo(String systemKey) { - if (!systemKey.equals(SYSTEM_KEY_CONFIG_CENTER)) { - return null; - } - List ccAddresses = ConfigUtil - .parseArrayValue(configuration.getString("servicecomb.config.client.serverUri")); - if (ccAddresses.isEmpty()) { - return null; - } - SystemBootstrapInfo cc = new SystemBootstrapInfo(); - cc.setAccessURL(ccAddresses); - return cc; - } - - @VisibleForTesting - public static void setConfiguration(AbstractConfiguration configuration) { - ConfigCenterDefaultDeploymentProvider.configuration = configuration; - } -} diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/collect/ConfigCenterInformationCollector.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/collect/ConfigCenterInformationCollector.java deleted file mode 100644 index 89568debc29..00000000000 --- a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/collect/ConfigCenterInformationCollector.java +++ /dev/null @@ -1,41 +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.servicecomb.config.collect; - -import org.apache.servicecomb.core.bootup.BootUpInformationCollector; -import org.apache.servicecomb.deployment.Deployment; -import org.apache.servicecomb.deployment.SystemBootstrapInfo; - -import java.util.Objects; - - -public class ConfigCenterInformationCollector implements BootUpInformationCollector { - @Override - public String collect() { - return "Config Center: " + getCenterInfo(Deployment.getSystemBootStrapInfo("ConfigCenter")); - } - - @Override - public int getOrder() { - return 1; - } - - private String getCenterInfo(SystemBootstrapInfo systemBootstrapInfo) { - return Objects.isNull(systemBootstrapInfo) ? "not exist" : systemBootstrapInfo.getAccessURL().toString(); - } -} diff --git a/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource b/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource new file mode 100644 index 00000000000..94d24b08be8 --- /dev/null +++ b/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource @@ -0,0 +1,18 @@ +# +# 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.servicecomb.config.cc.ConfigCenterDynamicPropertiesSource diff --git a/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource b/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource deleted file mode 100644 index d38ec076ab0..00000000000 --- a/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.config.ConfigCenterConfigurationSourceImpl \ No newline at end of file diff --git a/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector b/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector index 58062576e91..9b7c91cf3d9 100644 --- a/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector +++ b/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -org.apache.servicecomb.config.collect.ConfigCenterInformationCollector \ No newline at end of file +org.apache.servicecomb.config.cc.ConfigCenterInformationCollector diff --git a/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.deployment.DeploymentProvider b/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.deployment.DeploymentProvider deleted file mode 100644 index 39efa46f702..00000000000 --- a/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.deployment.DeploymentProvider +++ /dev/null @@ -1,17 +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. -# -org.apache.servicecomb.config.collect.ConfigCenterDefaultDeploymentProvider \ No newline at end of file diff --git a/dynamic-config/config-cc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/dynamic-config/config-cc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..febb8943865 --- /dev/null +++ b/dynamic-config/config-cc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.config.cc.ConfigCenterConfiguration diff --git a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/center/client/ConfigCenterAddressManagerTest.java b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterAddressManagerTest.java similarity index 81% rename from dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/center/client/ConfigCenterAddressManagerTest.java rename to dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterAddressManagerTest.java index 86b3e305db7..50d39bc19db 100644 --- a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/center/client/ConfigCenterAddressManagerTest.java +++ b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterAddressManagerTest.java @@ -15,13 +15,15 @@ * limitations under the License. */ -package org.apache.servicecomb.config.center.client; +package org.apache.servicecomb.config.cc; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.servicecomb.config.center.client.ConfigCenterAddressManager; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -36,11 +38,17 @@ class ConfigCenterAddressManagerTest { private static ConfigCenterAddressManager addressManager2; @Test - public void addressManagerTest() { + public void addressManagerTest() throws NoSuchFieldException, IllegalAccessException { addresses.add("http://127.0.0.1:30103"); addresses.add("https://127.0.0.2:30103"); - addressManager1 = new ConfigCenterAddressManager("project", addresses, new EventBus()); - addressManager2 = new ConfigCenterAddressManager(null, addresses, new EventBus()); + addressManager1 = new ConfigCenterAddressManager("project", addresses, new EventBus(), "", ""); + addressManager2 = new ConfigCenterAddressManager(null, addresses, new EventBus(), "", ""); + Field addressManagerField = addressManager1.getClass().getSuperclass().getDeclaredField("index"); + addressManagerField.setAccessible(true); + addressManagerField.set(addressManager1, 0); + addressManagerField = addressManager2.getClass().getSuperclass().getDeclaredField("index"); + addressManagerField.setAccessible(true); + addressManagerField.set(addressManager2, 0); Assertions.assertNotNull(addressManager1); Assertions.assertNotNull(addressManager2); @@ -63,7 +71,7 @@ public void onRefreshEndpointEvent() { Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); - addressManager1 = new ConfigCenterAddressManager("project", addresses, new EventBus()); + addressManager1 = new ConfigCenterAddressManager("project", addresses, new EventBus(), "", ""); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseConfigCenter"); addressManager1.refreshEndpoint(event, "CseConfigCenter"); diff --git a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/ConfigCenterConfigurationSourceImplTest.java b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterConfigurationSourceImplTest.java similarity index 78% rename from dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/ConfigCenterConfigurationSourceImplTest.java rename to dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterConfigurationSourceImplTest.java index 76f0cb49f42..3298d95bb19 100644 --- a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/ConfigCenterConfigurationSourceImplTest.java +++ b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterConfigurationSourceImplTest.java @@ -15,9 +15,10 @@ * limitations under the License. */ -package org.apache.servicecomb.config; +package org.apache.servicecomb.config.cc; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -30,13 +31,16 @@ import org.junit.jupiter.api.Test; class ConfigCenterConfigurationSourceImplTest { - @Test - void configAddressManagerTest() { + void configAddressManagerTest() throws IllegalAccessException, NoSuchFieldException { List addresses = new ArrayList<>(); addresses.add("http://127.0.0.1:30103"); addresses.add("http://127.0.0.2:30103"); - ConfigCenterAddressManager addressManager = new ConfigCenterAddressManager("test", addresses, EventManager.getEventBus()); + ConfigCenterAddressManager addressManager = new ConfigCenterAddressManager("test", addresses, + EventManager.getEventBus(), "", ""); + Field addressManagerField = addressManager.getClass().getSuperclass().getDeclaredField("index"); + addressManagerField.setAccessible(true); + addressManagerField.set(addressManager, 0); Assertions.assertNotNull(addressManager); String address = addressManager.address(); @@ -44,7 +48,10 @@ void configAddressManagerTest() { address = addressManager.address(); Assertions.assertEquals("http://127.0.0.1:30103/v3/test", address); - addressManager = new ConfigCenterAddressManager(null, addresses, EventManager.getEventBus()); + addressManager = new ConfigCenterAddressManager(null, addresses, EventManager.getEventBus(), "", ""); + addressManagerField = addressManager.getClass().getSuperclass().getDeclaredField("index"); + addressManagerField.setAccessible(true); + addressManagerField.set(addressManager, 0); address = addressManager.address(); Assertions.assertEquals("http://127.0.0.2:30103/v3/default", address); } @@ -59,7 +66,8 @@ void onRefreshEndpointEventTest() { zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", new ArrayList<>()); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseConfigCenter"); - ConfigCenterAddressManager addressManager = new ConfigCenterAddressManager("test", addresses, EventManager.getEventBus()); + ConfigCenterAddressManager addressManager = new ConfigCenterAddressManager("test", addresses, + EventManager.getEventBus(), "", ""); addressManager.onRefreshEndpointEvent(event); List availableAZ = addressManager.getAvailableZone(); diff --git a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/collect/TestConfigCenterDefaultDeploymentProvider.java b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/collect/TestConfigCenterDefaultDeploymentProvider.java deleted file mode 100644 index 540ac2c524a..00000000000 --- a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/collect/TestConfigCenterDefaultDeploymentProvider.java +++ /dev/null @@ -1,67 +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.servicecomb.config.collect; - -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.deployment.Deployment; -import org.apache.servicecomb.deployment.SystemBootstrapInfo; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class TestConfigCenterDefaultDeploymentProvider { - @BeforeEach - public void start() { - ArchaiusUtils.resetConfig(); - } - - @AfterEach - public void tearDown() { - ArchaiusUtils.resetConfig(); - } - - @Test - public void testConfiguration() { - ConfigCenterDefaultDeploymentProvider.setConfiguration(ConfigUtil.createLocalConfig()); - SystemBootstrapInfo info = Deployment.getSystemBootStrapInfo(ConfigCenterDefaultDeploymentProvider.SYSTEM_KEY_CONFIG_CENTER); - Assertions.assertEquals("https://172.16.8.7:30103", info.getAccessURL().get(0)); - Assertions.assertNull(Deployment.getSystemBootStrapInfo("wrong")); - } - - @Test - public void testConfigurationEnv() { - System.setProperty("servicecomb.config.client.serverUri", "https://localhost:9988"); - ConfigCenterDefaultDeploymentProvider.setConfiguration(ConfigUtil.createLocalConfig()); - SystemBootstrapInfo info = Deployment.getSystemBootStrapInfo(ConfigCenterDefaultDeploymentProvider.SYSTEM_KEY_CONFIG_CENTER); - Assertions.assertEquals("https://localhost:9988", info.getAccessURL().get(0)); - System.getProperties().remove("servicecomb.config.client.serverUri"); - } - - @Test - public void testConfigurationEnvTwo() { - System.setProperty("servicecomb.config.client.serverUri", "https://localhost:9988,https://localhost:9987"); - ConfigCenterDefaultDeploymentProvider.setConfiguration(ConfigUtil.createLocalConfig()); - SystemBootstrapInfo info = Deployment.getSystemBootStrapInfo(ConfigCenterDefaultDeploymentProvider.SYSTEM_KEY_CONFIG_CENTER); - Assertions.assertEquals(2, info.getAccessURL().size()); - Assertions.assertEquals("https://localhost:9988", info.getAccessURL().get(0)); - Assertions.assertEquals("https://localhost:9987", info.getAccessURL().get(1)); - System.getProperties().remove("servicecomb.config.client.serverUri"); - } -} diff --git a/dynamic-config/config-consul/pom.xml b/dynamic-config/config-consul/pom.xml new file mode 100644 index 00000000000..af890c0e6ec --- /dev/null +++ b/dynamic-config/config-consul/pom.xml @@ -0,0 +1,54 @@ + + + + + 4.0.0 + + org.apache.servicecomb + dynamic-config + 3.4.0-SNAPSHOT + + + config-consul + Java Chassis::Dynamic Config::Consul + + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + java-chassis-core + + + org.kiwiproject + consul-client + + + com.google.code.gson + gson + + + diff --git a/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfig.java b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfig.java new file mode 100644 index 00000000000..57613037eb4 --- /dev/null +++ b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfig.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config.consul; + +import org.springframework.core.env.Environment; + +public class ConsulConfig { + + public static final String CONSUL_CONFIG_PREFIX = "servicecomb.config.consul"; + + public static final String CONSUL_DEFAULT_ENVIRONMENT = "production"; + + public static final String PROPERTY_CONSUL_HOST = "servicecomb.config.consul.host"; + + public static final String PROPERTY_CONSUL_PORT = "servicecomb.config.consul.port"; + + public static final String PROPERTY_CONSUL_SCHEME = "servicecomb.config.consul.scheme"; + + public static final String PROPERTY_CONSUL_ACL_TOKEN = "servicecomb.config.consul.acl-token"; + + public static final String PROPERTY_CONSUL_WATCH_SECONDS = "servicecomb.config.consul.watch-seconds"; + + public static final String PROPERTY_INSTANCE_TAG = "servicecomb.config.consul.instance-tag"; + + public static final String PATH_ENVIRONMENT = "/servicecomb/config/environment/%s"; + + public static final String PATH_APPLICATION = "/servicecomb/config/application/%s/%s"; + + public static final String PATH_SERVICE = "/servicecomb/config/service/%s/%s/%s"; + + public static final String PATH_VERSION = "/servicecomb/config/version/%s/%s/%s/%s"; + + public static final String PATH_TAG = "/servicecomb/config/tag/%s/%s/%s/%s/%s"; + + private final Environment environment; + + public ConsulConfig(Environment environment) { + this.environment = environment; + } + + public String getConsulHost() { + return environment.getProperty(PROPERTY_CONSUL_HOST, String.class, "127.0.0.1"); + } + + public int getConsulPort() { + return environment.getProperty(PROPERTY_CONSUL_PORT, int.class, 8500); + } + + public String getConsulScheme() { + return environment.getProperty(PROPERTY_CONSUL_SCHEME); + } + + public String getConsulAclToken() { + return environment.getProperty(PROPERTY_CONSUL_ACL_TOKEN); + } + + public int getConsulWatchSeconds() { + return environment.getProperty(PROPERTY_CONSUL_WATCH_SECONDS, int.class, 8); + } + + public String getInstanceTag() { + return environment.getProperty(PROPERTY_INSTANCE_TAG); + } +} diff --git a/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigClient.java b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigClient.java new file mode 100644 index 00000000000..ae18e11ff48 --- /dev/null +++ b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigClient.java @@ -0,0 +1,305 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config.consul; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.jetbrains.annotations.NotNull; +import org.kiwiproject.consul.Consul; +import org.kiwiproject.consul.KeyValueClient; +import org.kiwiproject.consul.cache.KVCache; +import org.kiwiproject.consul.model.kv.Value; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ByteArrayResource; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; + +import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_APPLICATION; +import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_ENVIRONMENT; +import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_SERVICE; +import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_TAG; +import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_VERSION; + +public class ConsulConfigClient { + private static final Logger LOGGER = LoggerFactory.getLogger(ConsulConfigClient.class); + + public class GetDataRunnable implements Runnable { + + private Map dataMap; + + private String path; + + public GetDataRunnable(String path, Map dataMap) { + this.dataMap = dataMap; + this.path = path; + } + + @Override + public void run() { + try { + if (path.equals("tagData")) { + tagData = dataMap; + } else if (path.equals("versionData")) { + versionData = dataMap; + } else if (path.equals("serviceData")) { + serviceData = dataMap; + } else if (path.equals("applicationData")) { + applicationData = dataMap; + } else { + environmentData = dataMap; + } + refreshConfigItems(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private ConsulDynamicPropertiesSource.UpdateHandler updateHandler; + + private ConsulConfig consulConfig; + + private Environment environment; + + private final Object lock = new Object(); + + private Consul consulClient; + + private KeyValueClient kvClient; + + private ConsulConfigProperties consulConfigProperties; + + private Map environmentData = new HashMap<>(); + + private Map applicationData = new HashMap<>(); + + private Map serviceData = new HashMap<>(); + + private Map versionData = new HashMap<>(); + + private Map tagData = new HashMap<>(); + + private Map allLast = new HashMap<>(); + + public ConsulConfigClient(ConsulDynamicPropertiesSource.UpdateHandler updateHandler, Environment environment, ConsulConfigProperties consulConfigProperties, Consul consulClient) { + this.updateHandler = updateHandler; + this.consulConfig = new ConsulConfig(environment); + this.environment = environment; + this.consulConfigProperties = consulConfigProperties; + this.consulClient = consulClient; + this.kvClient = consulClient.keyValueClient(); + } + + public void refreshConsulConfig() { + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = ConsulConfig.CONSUL_DEFAULT_ENVIRONMENT; + } + addEnvironmentConfig(env); + addApplicationConfig(env); + addServiceConfig(env); + addVersionConfig(env); + addTagConfig(env); + + refreshConfigItems(); + } + + private void addTagConfig(String env) { + if (StringUtils.isBlank(consulConfig.getInstanceTag())) { + return; + } + String path = String.format(PATH_TAG, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment), + consulConfig.getInstanceTag()); + + this.tagData = parseData(path); + try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { + cache.addListener(newValues -> { + Optional newValue = newValues.values().stream() + .filter(value -> value.getKey().equals(path)) + .findAny(); + + newValue.ifPresent(value -> { + Optional decodedValue = newValue.get().getValueAsString(); + decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("tagData", getValues(path))).start()); + }); + }); + cache.start(); + } + } + + private void addVersionConfig(String env) { + String path = String.format(PATH_VERSION, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment)); + + this.versionData = parseData(path); + try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { + cache.addListener(newValues -> { + Optional newValue = newValues.values().stream() + .filter(value -> value.getKey().equals(path)) + .findAny(); + + newValue.ifPresent(value -> { + Optional decodedValue = newValue.get().getValueAsString(); + decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("versionData", getValues(path))).start()); + }); + }); + cache.start(); + } + } + + private void addServiceConfig(String env) { + String path = String.format(PATH_SERVICE, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment)); + + this.serviceData = parseData(path); + try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { + cache.addListener(newValues -> { + Optional newValue = newValues.values().stream() + .filter(value -> value.getKey().equals(path)) + .findAny(); + + newValue.ifPresent(value -> { + Optional decodedValue = newValue.get().getValueAsString(); + decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("serviceData", getValues(path))).start()); + }); + }); + cache.start(); + } + } + + private void addApplicationConfig(String env) { + String path = String.format(PATH_APPLICATION, env, BootStrapProperties.readApplication(environment)); + this.applicationData = parseData(path); + try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { + cache.addListener(newValues -> { + Optional newValue = newValues.values().stream() + .filter(value -> value.getKey().equals(path)) + .findAny(); + + newValue.ifPresent(value -> { + Optional decodedValue = newValue.get().getValueAsString(); + decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("applicationData", getValues(path))).start()); + }); + }); + cache.start(); + } + } + + private void addEnvironmentConfig(String env) { + String path = String.format(PATH_ENVIRONMENT, env); + + this.environmentData = parseData(path); + try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { + cache.addListener(newValues -> { + Optional newValue = newValues.values().stream() + .filter(value -> value.getKey().equals(path)) + .findAny(); + + newValue.ifPresent(value -> { + Optional decodedValue = newValue.get().getValueAsString(); + decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("environmentData", getValues(path))).start()); + }); + }); + cache.start(); + } + } + + public Map parseData(String path) { + try { + return getValues(path); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + } + return new HashMap<>(); + } + + private Map getValues(String path) { + Map values = new HashMap<>(); + KeyValueClient keyValueClient = consulClient.keyValueClient(); + String decodedValue = keyValueClient.getValueAsString(path).orElseThrow(); + if (StringUtils.isBlank(decodedValue)) { + return values; + } + return getValues(path, decodedValue); + } + + private @NotNull Map getValues(String path, String decodedValue) { + Map values = new HashMap<>(); + if (path.endsWith(".yaml") || path.endsWith(".yml")) { + YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); + yamlFactory.setResources(new ByteArrayResource(decodedValue.getBytes(StandardCharsets.UTF_8))); + values.putAll(toMap(yamlFactory.getObject())); + } else if (path.endsWith(".properties")) { + Properties properties = new Properties(); + try { + properties.load(new StringReader(decodedValue)); + } catch (IOException e) { + LOGGER.error(e.getMessage(), e); + } + values.putAll(toMap(properties)); + } else { + values.put(path, decodedValue); + } + return values; + } + + private void refreshConfigItems() { + synchronized (lock) { + Map all = new HashMap<>(); + all.putAll(environmentData); + all.putAll(applicationData); + all.putAll(serviceData); + all.putAll(versionData); + all.putAll(tagData); + updateHandler.handle(all, allLast); + this.allLast = all; + } + } + + @SuppressWarnings("unchecked") + private Map toMap(Properties properties) { + if (properties == null) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(); + Enumeration keys = (Enumeration) properties.propertyNames(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + Object value = properties.getProperty(key); + result.put(key, value); + } + return result; + } +} diff --git a/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigProperties.java b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigProperties.java new file mode 100644 index 00000000000..fe3a278678a --- /dev/null +++ b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigProperties.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config.consul; + +public class ConsulConfigProperties { + + private String host = "localhost"; + + private Integer port = 8500; + + private String scheme = "http"; + + private String aclToken; + + private Integer watchSeconds = 8; + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getScheme() { + return scheme; + } + + public void setScheme(String scheme) { + this.scheme = scheme; + } + + public String getAclToken() { + return aclToken; + } + + public void setAclToken(String aclToken) { + this.aclToken = aclToken; + } + + public Integer getWatchSeconds() { + return watchSeconds; + } + + public void setWatchSeconds(Integer watchSeconds) { + this.watchSeconds = watchSeconds; + } +} diff --git a/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulDynamicPropertiesSource.java b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulDynamicPropertiesSource.java new file mode 100644 index 00000000000..9f0b3649249 --- /dev/null +++ b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulDynamicPropertiesSource.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.servicecomb.config.consul; + +import com.google.common.net.HostAndPort; +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.config.DynamicPropertiesSource; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.kiwiproject.consul.Consul; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ConsulDynamicPropertiesSource implements DynamicPropertiesSource { + private static final Logger LOGGER = LoggerFactory.getLogger(ConsulDynamicPropertiesSource.class); + + public static final String SOURCE_NAME = "consul"; + + private final Map valueCache = new ConcurrentHashMap<>(); + + private ConsulConfigClient consulConfigClient; + + public ConsulDynamicPropertiesSource() { + } + + private final UpdateHandler updateHandler = new UpdateHandler(); + public class UpdateHandler { + public void handle(Map current, Map last) { + ConfigurationChangedEvent event = ConfigurationChangedEvent.createIncremental(current, last); + LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); + valueCache.putAll(event.getAdded()); + valueCache.putAll(event.getUpdated()); + event.getDeleted().forEach((k, v) -> valueCache.remove(k)); + EventManager.post(event); + } + } + + private ConsulConfigProperties consulConfigProperties(Environment environment) { + ConsulConfig consulConfig = new ConsulConfig(environment); + ConsulConfigProperties consulConfigProperties = new ConsulConfigProperties(); + consulConfigProperties.setHost(consulConfig.getConsulHost()); + consulConfigProperties.setPort(consulConfig.getConsulPort()); + consulConfigProperties.setScheme(consulConfig.getConsulScheme()); + consulConfigProperties.setAclToken(consulConfig.getConsulAclToken()); + consulConfigProperties.setWatchSeconds(consulConfig.getConsulWatchSeconds()); + return consulConfigProperties; + } + + private Consul consulClient(ConsulConfigProperties consulConfigProperties) { + Consul.Builder builder = Consul.builder().withHostAndPort(HostAndPort.fromParts(consulConfigProperties.getHost(), consulConfigProperties.getPort())); + if (StringUtils.isNotBlank(consulConfigProperties.getAclToken())) { + builder.withAclToken(consulConfigProperties.getAclToken()); + } + return builder.build(); + } + + private ConsulConfigClient consulConfigClient(Environment environment) { + ConsulConfigProperties consulConfigProperties = consulConfigProperties(environment); + Consul consulClient = consulClient(consulConfigProperties); + return new ConsulConfigClient(updateHandler, environment, consulConfigProperties, consulClient); + } + + @Override + public PropertySource create(Environment environment) { + try { + consulConfigClient = consulConfigClient(environment); + consulConfigClient.refreshConsulConfig(); + } catch (Exception e) { + throw new IllegalStateException("Set up consul config failed.", e); + } + return new MapPropertySource(SOURCE_NAME, valueCache); + } + + @Override + public int getOrder() { + return 0; + } +} diff --git a/dynamic-config/config-consul/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource b/dynamic-config/config-consul/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource new file mode 100644 index 00000000000..7b1c73e1195 --- /dev/null +++ b/dynamic-config/config-consul/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource @@ -0,0 +1,18 @@ +# +# 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.servicecomb.config.consul.ConsulDynamicPropertiesSource diff --git a/dynamic-config/config-etcd/pom.xml b/dynamic-config/config-etcd/pom.xml new file mode 100644 index 00000000000..8aa6b2cf963 --- /dev/null +++ b/dynamic-config/config-etcd/pom.xml @@ -0,0 +1,44 @@ + + + + + + + dynamic-config + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + config-etcd + Java Chassis::Dynamic Config::Zookeeper + + + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-vertx + + + io.etcd + jetcd-core + + + diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java new file mode 100644 index 00000000000..9a7743add07 --- /dev/null +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java @@ -0,0 +1,275 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.etcd; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.etcd.EtcdDynamicPropertiesSource.UpdateHandler; +import org.apache.servicecomb.foundation.common.utils.MuteExceptionUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ByteArrayResource; + +import io.etcd.jetcd.ByteSequence; +import io.etcd.jetcd.Client; +import io.etcd.jetcd.KeyValue; +import io.etcd.jetcd.Watch; +import io.etcd.jetcd.kv.GetResponse; +import io.etcd.jetcd.options.GetOption; +import io.etcd.jetcd.options.WatchOption; + +public class EtcdClient { + + public class GetDataRunnable implements Runnable { + + private Map dataMap; + + private EtcdClient etcdClient; + + private String path; + + public GetDataRunnable(Map dataMap, EtcdClient etcdClient, String path) { + this.dataMap = dataMap; + this.etcdClient = etcdClient; + this.path = path; + } + + @Override + public void run() { + try { + dataMap.clear(); + dataMap.putAll(etcdClient.parseData(path)); + refreshConfigItems(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdClient.class); + + public static final String PATH_ENVIRONMENT = "/servicecomb/config/environment/%s"; + + public static final String PATH_APPLICATION = "/servicecomb/config/application/%s/%s"; + + public static final String PATH_SERVICE = "/servicecomb/config/service/%s/%s/%s"; + + public static final String PATH_VERSION = "/servicecomb/config/version/%s/%s/%s/%s"; + + public static final String PATH_TAG = "/servicecomb/config/tag/%s/%s/%s/%s/%s"; + + private final UpdateHandler updateHandler; + + private final EtcdConfig etcdConfig; + + private final Environment environment; + + private final Object lock = new Object(); + + private Map environmentData = new HashMap<>(); + + private Map applicationData = new HashMap<>(); + + private Map serviceData = new HashMap<>(); + + private Map versionData = new HashMap<>(); + + private Map tagData = new HashMap<>(); + + private Map allLast = new HashMap<>(); + + private Client client; + + public EtcdClient(UpdateHandler updateHandler, Environment environment) { + this.updateHandler = updateHandler; + this.etcdConfig = new EtcdConfig(environment); + this.environment = environment; + } + + public void getClient() { + if (StringUtils.isEmpty(etcdConfig.getAuthInfo())) { + this.client = Client.builder().endpoints(etcdConfig.getConnectString()).build(); + } else { + String[] authInfo = etcdConfig.getAuthInfo().split(":"); + this.client = Client.builder().endpoints(etcdConfig.getConnectString()) + .user(ByteSequence.from(authInfo[0], StandardCharsets.UTF_8)) + .password(ByteSequence.from(authInfo[1], StandardCharsets.UTF_8)).build(); + } + } + + public void refreshEtcdConfig() throws Exception { + + getClient(); + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = EtcdConfig.ZOOKEEPER_DEFAULT_ENVIRONMENT; + } + addEnvironmentConfig(env); + addApplicationConfig(env); + addServiceConfig(env); + addVersionConfig(env); + addTagConfig(env); + + refreshConfigItems(); + } + + private void addTagConfig(String env) throws Exception { + if (StringUtils.isEmpty(etcdConfig.getInstanceTag())) { + return; + } + String path = String.format(PATH_TAG, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment), + etcdConfig.getInstanceTag()); + + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunnable(tagData, this, path)).start()); + this.tagData = parseData(path); + } + + private void addVersionConfig(String env) throws Exception { + String path = String.format(PATH_VERSION, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment)); + + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunnable(versionData, this, path)).start()); + this.versionData = parseData(path); + } + + private void addServiceConfig(String env) throws Exception { + String path = String.format(PATH_SERVICE, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment)); + + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunnable(serviceData, this, path)).start()); + this.serviceData = parseData(path); + } + + private void addApplicationConfig(String env) throws Exception { + String path = String.format(PATH_APPLICATION, env, BootStrapProperties.readApplication(environment)); + + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunnable(applicationData, this, path)).start()); + this.applicationData = parseData(path); + } + + private void addEnvironmentConfig(String env) throws Exception { + String path = String.format(PATH_ENVIRONMENT, env); + + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunnable(environmentData, this, path)).start()); + this.environmentData = parseData(path); + } + + public Map parseData(String path) throws Exception { + + List endpointKv = getValuesByPrefix(path); + return getValues(path, endpointKv); + } + + private Map getValues(String path, List endpointKv) { + Map values = new HashMap<>(); + for (KeyValue keyValue : endpointKv) { + String key = new String(keyValue.getKey().getBytes(), StandardCharsets.UTF_8); + String value = new String(keyValue.getValue().getBytes(), StandardCharsets.UTF_8); + if (key.equals(path)) { + continue; + } + if (key.endsWith(".yaml") || key.endsWith(".yml")) { + YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); + yamlFactory.setResources(new ByteArrayResource(value.getBytes(StandardCharsets.UTF_8))); + values.putAll(toMap(yamlFactory.getObject())); + } else if (key.endsWith(".properties")) { + Properties properties = new Properties(); + try { + properties.load(new StringReader(value)); + } catch (IOException e) { + LOGGER.error("load error"); + } + values.putAll(toMap(properties)); + } else { + values.put(key, value); + } + } + return values; + } + + private List getValuesByPrefix(String prefix) { + + CompletableFuture getFuture = client.getKVClient() + .get(ByteSequence.from(prefix, StandardCharsets.UTF_8), + GetOption.builder().withPrefix(ByteSequence.from(prefix, StandardCharsets.UTF_8)).build()); + GetResponse response = MuteExceptionUtil.builder().withLog("get kv by prefix error") + .executeCompletableFuture(getFuture); + return response.getKvs(); + } + + private void refreshConfigItems() { + synchronized (lock) { + Map all = new HashMap<>(); + all.putAll(environmentData); + all.putAll(applicationData); + all.putAll(serviceData); + all.putAll(versionData); + all.putAll(tagData); + updateHandler.handle(all, allLast); + this.allLast = all; + } + } + + @SuppressWarnings("unchecked") + private Map toMap(Properties properties) { + if (properties == null) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(); + Enumeration keys = (Enumeration) properties.propertyNames(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + Object value = properties.getProperty(key); + result.put(key, value); + } + return result; + } +} diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdConfig.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdConfig.java new file mode 100644 index 00000000000..ee04204c28a --- /dev/null +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdConfig.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.servicecomb.config.etcd; + +import org.springframework.core.env.Environment; + +public class EtcdConfig { + public static final String ZOOKEEPER_DEFAULT_ENVIRONMENT = "production"; + + public static final String PROPERTY_CONNECT_STRING = "servicecomb.config.etcd.connectString"; + + public static final String PROPERTY_SESSION_TIMEOUT = "servicecomb.config.etcd.sessionTimeoutMillis"; + + public static final String PROPERTY_CONNECTION_TIMEOUT = "servicecomb.config.etcd.connectionTimeoutMills"; + + public static final String PROPERTY_AUTH_SCHEMA = "servicecomb.config.etcd.authenticationSchema"; + + public static final String PROPERTY_AUTH_INFO = "servicecomb.config.etcd.authenticationInfo"; + + public static final String PROPERTY_INSTANCE_TAG = "servicecomb.config.etcd.instanceTag"; + + private final Environment environment; + + public EtcdConfig(Environment environment) { + this.environment = environment; + } + + public String getConnectString() { + return environment.getProperty(PROPERTY_CONNECT_STRING, "http://127.0.0.1:2379"); + } + + public int getSessionTimeoutMillis() { + return environment.getProperty(PROPERTY_SESSION_TIMEOUT, int.class, 60000); + } + + public int getConnectionTimeoutMillis() { + return environment.getProperty(PROPERTY_CONNECTION_TIMEOUT, int.class, 1000); + } + + public String getAuthSchema() { + return environment.getProperty(PROPERTY_AUTH_SCHEMA); + } + + public String getAuthInfo() { + return environment.getProperty(PROPERTY_AUTH_INFO); + } + + public String getInstanceTag() { + return environment.getProperty(PROPERTY_INSTANCE_TAG); + } +} diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java new file mode 100644 index 00000000000..a86de892180 --- /dev/null +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.etcd; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.config.DynamicPropertiesSource; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +public class EtcdDynamicPropertiesSource implements DynamicPropertiesSource { + public static final String SOURCE_NAME = "etcd"; + + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdDynamicPropertiesSource.class); + + private final Map valueCache = new ConcurrentHashMap<>(); + + public EtcdDynamicPropertiesSource() { + } + + private final UpdateHandler updateHandler = new UpdateHandler(); + + private void init(Environment environment) { + EtcdClient etcdClient = new EtcdClient(updateHandler, environment); + try { + etcdClient.refreshEtcdConfig(); + } catch (Exception e) { + throw new IllegalStateException("Set up etcd config failed.", e); + } + } + + public class UpdateHandler { + public void handle(Map current, Map last) { + ConfigurationChangedEvent event = ConfigurationChangedEvent.createIncremental(current, last); + LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); + valueCache.putAll(event.getAdded()); + valueCache.putAll(event.getUpdated()); + event.getDeleted().forEach((k, v) -> valueCache.remove(k)); + EventManager.post(event); + } + } + + @Override + public PropertySource create(Environment environment) { + init(environment); + return new MapPropertySource(SOURCE_NAME, valueCache); + } + + @Override + public int getOrder() { + return 0; + } +} diff --git a/dynamic-config/config-etcd/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource b/dynamic-config/config-etcd/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource new file mode 100644 index 00000000000..d10124395fa --- /dev/null +++ b/dynamic-config/config-etcd/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource @@ -0,0 +1,18 @@ +# +# 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.servicecomb.config.etcd.EtcdDynamicPropertiesSource diff --git a/dynamic-config/config-kie/pom.xml b/dynamic-config/config-kie/pom.xml index 513c0641f74..4bf61b5801f 100644 --- a/dynamic-config/config-kie/pom.xml +++ b/dynamic-config/config-kie/pom.xml @@ -23,7 +23,7 @@ dynamic-config org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -35,10 +35,6 @@ org.apache.servicecomb foundation-config - - org.apache.servicecomb - deployment - org.apache.servicecomb foundation-ssl @@ -66,4 +62,4 @@ config-kie-client - \ No newline at end of file + diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfig.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfig.java index 872165f51eb..79e40786f8f 100644 --- a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfig.java +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfig.java @@ -17,22 +17,13 @@ package org.apache.servicecomb.config.kie; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.foundation.vertx.VertxConst; - -import com.netflix.config.ConcurrentCompositeConfiguration; +import org.springframework.core.env.Environment; public class KieConfig { - - public static final KieConfig INSTANCE = new KieConfig(); - - private static ConcurrentCompositeConfiguration finalConfig; - public static final String SSL_TAG = "kie.consumer"; private static final String SERVER_URL_KEY = "servicecomb.kie.serverUri"; @@ -75,114 +66,104 @@ public class KieConfig { private static final String CUSTOM_LABEL_VALUE_DEFAULT = ""; - private KieConfig() { - } + private static final String CLIENT_CONNECT_TIMEOUT = "servicecomb.kie.client.timeout.connect"; - public static ConcurrentCompositeConfiguration getFinalConfig() { - return finalConfig; - } - - public static void setFinalConfig(ConcurrentCompositeConfiguration finalConfig) { - KieConfig.finalConfig = finalConfig; - } - - @SuppressWarnings("unchecked") - public List getFileSources() { - Object property = finalConfig.getProperty(FILE_SOURCE); - if (property instanceof String) { - return new ArrayList<>(Arrays.asList(((String) property).split(","))); - } - if (property instanceof List) { - return (List) property; - } - return Collections.EMPTY_LIST; - } + private static final String CLIENT_REQUEST_TIMEOUT = "servicecomb.kie.client.timeout.request"; - public String getVersion() { - return BootStrapProperties.readServiceVersion(finalConfig); - } + private static final String CLIENT_SOCKET_TIMEOUT = "servicecomb.kie.client.timeout.socket"; - public String getServiceName() { - return BootStrapProperties.readServiceName(finalConfig); - } + private final Environment environment; - public String getEnvironment() { - return BootStrapProperties.readServiceEnvironment(finalConfig); + public KieConfig(Environment environment) { + this.environment = environment; } - public String getAppName() { - return BootStrapProperties.readApplication(finalConfig); + @SuppressWarnings("unchecked") + public List getFileSources() { + return environment.getProperty(FILE_SOURCE, List.class, Collections.emptyList()); } public String getDomainName() { - return finalConfig.getString(DOMAIN_NAME, "default"); + return environment.getProperty(DOMAIN_NAME, "default"); } public String getServerUri() { - return finalConfig.getString(SERVER_URL_KEY); + return environment.getProperty(SERVER_URL_KEY); } public int getRefreshInterval() { - return finalConfig.getInt(REFRESH_INTERVAL, DEFAULT_REFRESH_INTERVAL); + return environment.getProperty(REFRESH_INTERVAL, int.class, DEFAULT_REFRESH_INTERVAL); } public int getFirstRefreshInterval() { - return finalConfig.getInt(FIRST_REFRESH_INTERVAL, DEFAULT_FIRST_REFRESH_INTERVAL); + return environment.getProperty(FIRST_REFRESH_INTERVAL, int.class, DEFAULT_FIRST_REFRESH_INTERVAL); } public boolean enableAppConfig() { - return finalConfig.getBoolean(ENABLE_APP_CONFIG, true); + return environment.getProperty(ENABLE_APP_CONFIG, boolean.class, true); } public boolean enableServiceConfig() { - return finalConfig.getBoolean(ENABLE_SERVICE_CONFIG, true); + return environment.getProperty(ENABLE_SERVICE_CONFIG, boolean.class, true); } public boolean enableVersionConfig() { - return finalConfig.getBoolean(ENABLE_VERSION_CONFIG, true); + return environment.getProperty(ENABLE_VERSION_CONFIG, boolean.class, true); } public boolean enableCustomConfig() { - return finalConfig.getBoolean(ENABLE_CUSTOM_CONFIG, true); + return environment.getProperty(ENABLE_CUSTOM_CONFIG, boolean.class, true); } public boolean enableLongPolling() { - return finalConfig.getBoolean(ENABLE_LONG_POLLING, DEFAULT_ENABLE_LONG_POLLING); + return environment.getProperty(ENABLE_LONG_POLLING, boolean.class, DEFAULT_ENABLE_LONG_POLLING); } public int getPollingWaitTime() { - return finalConfig.getInt(POLLING_WAIT_TIME, DEFAULT_POLLING_WAIT_TIME); + return environment.getProperty(POLLING_WAIT_TIME, int.class, DEFAULT_POLLING_WAIT_TIME); } public boolean firstPullRequired() { - return finalConfig.getBoolean(FIRST_PULL_REQUIRED, false); + return environment.getProperty(FIRST_PULL_REQUIRED, boolean.class, false); } public String getCustomLabel() { - return finalConfig.getString(CUSTOM_LABEL, CUSTOM_LABEL_DEFAULT); + return environment.getProperty(CUSTOM_LABEL, CUSTOM_LABEL_DEFAULT); } public String getCustomLabelValue() { - return finalConfig.getString(CUSTOM_LABEL_VALUE, CUSTOM_LABEL_VALUE_DEFAULT); + return environment.getProperty(CUSTOM_LABEL_VALUE, CUSTOM_LABEL_VALUE_DEFAULT); } public Boolean isProxyEnable() { - return finalConfig.getBoolean(VertxConst.PROXY_ENABLE, false); + return environment.getProperty(VertxConst.PROXY_ENABLE, boolean.class, false); } public String getProxyHost() { - return finalConfig.getString(VertxConst.PROXY_HOST, "127.0.0.1"); + return environment.getProperty(VertxConst.PROXY_HOST, "127.0.0.1"); } public int getProxyPort() { - return finalConfig.getInt(VertxConst.PROXY_PORT, 8080); + return environment.getProperty(VertxConst.PROXY_PORT, int.class, 8080); } public String getProxyUsername() { - return finalConfig.getString(VertxConst.PROXY_USERNAME, null); + return environment.getProperty(VertxConst.PROXY_USERNAME); } public String getProxyPasswd() { - return finalConfig.getString(VertxConst.PROXY_PASSWD, null); + return environment.getProperty(VertxConst.PROXY_PASSWD); + } + + public int getConnectTimeout() { + return environment.getProperty(CLIENT_CONNECT_TIMEOUT, int.class, 5000); + } + + public int getConnectionRequestTimeout() { + return environment.getProperty(CLIENT_REQUEST_TIMEOUT, int.class, 5000); + } + + public int getSocketTimeout() { + return environment.getProperty(CLIENT_SOCKET_TIMEOUT, int.class, 5000); } } diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfigConfiguration.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfigConfiguration.java new file mode 100644 index 00000000000..7b8e35bd6e4 --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfigConfiguration.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.servicecomb.config.kie; + +import org.apache.servicecomb.config.kie.collect.KieClientInformationCollector; +import org.springframework.context.annotation.Bean; + +public class KieConfigConfiguration { + @Bean + public KieClientInformationCollector kieClientInformationCollector() { + return new KieClientInformationCollector(); + } +} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfigurationSourceImpl.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfigurationSourceImpl.java deleted file mode 100644 index fbf700e8284..00000000000 --- a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfigurationSourceImpl.java +++ /dev/null @@ -1,210 +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.servicecomb.config.kie; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.apache.commons.configuration.Configuration; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpHost; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.servicecomb.config.common.ConfigConverter; -import org.apache.servicecomb.config.common.ConfigurationChangedEvent; -import org.apache.servicecomb.config.kie.client.KieClient; -import org.apache.servicecomb.config.kie.client.KieConfigManager; -import org.apache.servicecomb.config.kie.client.model.KieAddressManager; -import org.apache.servicecomb.config.kie.client.model.KieConfiguration; -import org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource; -import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; -import org.apache.servicecomb.foundation.common.event.EventManager; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; -import org.apache.servicecomb.http.client.common.HttpTransport; -import org.apache.servicecomb.http.client.common.HttpTransportFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.eventbus.Subscribe; -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.WatchedUpdateListener; -import com.netflix.config.WatchedUpdateResult; - -public class KieConfigurationSourceImpl implements ConfigCenterConfigurationSource { - - private static final Logger LOGGER = LoggerFactory.getLogger(KieConfigurationSourceImpl.class); - - private final List listeners = new CopyOnWriteArrayList<>(); - - private KieConfigManager kieConfigManager; - - private ConfigConverter configConverter; - - @Override - public int getOrder() { - return ORDER_BASE * 2; - } - - @Override - public boolean isValidSource(Configuration localConfiguration) { - KieConfig.setFinalConfig((ConcurrentCompositeConfiguration) localConfiguration); - - if (StringUtils.isEmpty(KieConfig.INSTANCE.getServerUri())) { - LOGGER.info("Kie server is not configured."); - return false; - } - return true; - } - - @Override - public void init(Configuration localConfiguration) { - configConverter = new ConfigConverter(KieConfig.INSTANCE.getFileSources()); - KieAddressManager kieAddressManager = configKieAddressManager(); - - RequestConfig.Builder requestBuilder = HttpTransportFactory.defaultRequestConfig(); - if (KieConfig.INSTANCE.enableLongPolling() - && KieConfig.INSTANCE.getPollingWaitTime() >= 0) { - requestBuilder.setConnectionRequestTimeout(KieConfig.INSTANCE.getPollingWaitTime() * 2 * 1000); - requestBuilder.setSocketTimeout(KieConfig.INSTANCE.getPollingWaitTime() * 2 * 1000); - } - HttpTransport httpTransport = createHttpTransport(kieAddressManager, requestBuilder.build(), - localConfiguration); - KieConfiguration kieConfiguration = createKieConfiguration(); - KieClient kieClient = new KieClient(kieAddressManager, httpTransport, kieConfiguration); - EventManager.register(this); - kieConfigManager = new KieConfigManager(kieClient, EventManager.getEventBus(), kieConfiguration, configConverter); - kieConfigManager.firstPull(); - kieConfigManager.startConfigKieManager(); - updateConfiguration(WatchedUpdateResult.createIncremental(configConverter.getCurrentData(), null, null)); - } - - @Subscribe - public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { - updateConfiguration( - WatchedUpdateResult.createIncremental(event.getAdded(), event.getUpdated(), event.getDeleted())); - } - - private KieConfiguration createKieConfiguration() { - return new KieConfiguration().setAppName(KieConfig.INSTANCE.getAppName()) - .setFirstPullRequired(KieConfig.INSTANCE.firstPullRequired()) - .setCustomLabel(KieConfig.INSTANCE.getCustomLabel()) - .setCustomLabelValue(KieConfig.INSTANCE.getCustomLabelValue()) - .setEnableAppConfig(KieConfig.INSTANCE.enableAppConfig()) - .setEnableCustomConfig(KieConfig.INSTANCE.enableCustomConfig()) - .setEnableLongPolling(KieConfig.INSTANCE.enableLongPolling()) - .setEnableServiceConfig(KieConfig.INSTANCE.enableServiceConfig()) - .setEnableVersionConfig(KieConfig.INSTANCE.enableVersionConfig()) - .setEnvironment(KieConfig.INSTANCE.getEnvironment()) - .setVersion(KieConfig.INSTANCE.getVersion()) - .setPollingWaitInSeconds(KieConfig.INSTANCE.getPollingWaitTime()) - .setProject(KieConfig.INSTANCE.getDomainName()) - .setRefreshIntervalInMillis(KieConfig.INSTANCE.getRefreshInterval()) - .setServiceName(KieConfig.INSTANCE.getServiceName()); - } - - private HttpTransport createHttpTransport(KieAddressManager kieAddressManager, RequestConfig requestConfig, - Configuration localConfiguration) { - List authHeaderProviders = SPIServiceUtils.getOrLoadSortedService(AuthHeaderProvider.class); - - if (KieConfig.INSTANCE.isProxyEnable()) { - HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). - setDefaultRequestConfig(requestConfig); - HttpHost proxy = new HttpHost(KieConfig.INSTANCE.getProxyHost(), - KieConfig.INSTANCE.getProxyPort(), "http"); // now only support http proxy - httpClientBuilder.setProxy(proxy); - CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(new AuthScope(proxy), - new UsernamePasswordCredentials(KieConfig.INSTANCE.getProxyUsername(), - KieConfig.INSTANCE.getProxyPasswd())); - httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); - - return HttpTransportFactory - .createHttpTransport( - TransportUtils - .createSSLProperties(kieAddressManager.sslEnabled(), localConfiguration, KieConfig.SSL_TAG), - getRequestAuthHeaderProvider(authHeaderProviders), httpClientBuilder); - } - - return HttpTransportFactory - .createHttpTransport( - TransportUtils - .createSSLProperties(kieAddressManager.sslEnabled(), localConfiguration, KieConfig.SSL_TAG), - getRequestAuthHeaderProvider(authHeaderProviders), requestConfig); - } - - private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List authHeaderProviders) { - return signRequest -> { - Map headers = new HashMap<>(); - authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders())); - return headers; - }; - } - - private KieAddressManager configKieAddressManager() { - return new KieAddressManager( - Arrays.asList(KieConfig.INSTANCE.getServerUri().split(",")), EventManager.getEventBus()); - } - - private void updateConfiguration(WatchedUpdateResult result) { - LOGGER.info("configuration changed keys, added=[{}], updated=[{}], deleted=[{}]", - result.getAdded() == null ? "" : result.getAdded().keySet(), - result.getChanged() == null ? "" : result.getChanged().keySet(), - result.getDeleted() == null ? "" : result.getDeleted().keySet()); - - for (WatchedUpdateListener l : listeners) { - try { - l.updateConfiguration(result); - } catch (Throwable ex) { - LOGGER.error("Error in invoking WatchedUpdateListener", ex); - } - } - } - - @Override - public void destroy() { - if (kieConfigManager == null) { - return; - } - kieConfigManager.stop(); - } - - @Override - public void addUpdateListener(WatchedUpdateListener watchedUpdateListener) { - listeners.add(watchedUpdateListener); - } - - @Override - public void removeUpdateListener(WatchedUpdateListener watchedUpdateListener) { - listeners.remove(watchedUpdateListener); - } - - @Override - public Map getCurrentData() throws Exception { - // data will updated by first pull, set empty to DynamicWatchedConfiguration first. - return Collections.emptyMap(); - } -} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieDynamicPropertiesSource.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieDynamicPropertiesSource.java new file mode 100644 index 00000000000..6683b84fb09 --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieDynamicPropertiesSource.java @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.kie; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.config.RequestConfig.Builder; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.config.DynamicPropertiesSource; +import org.apache.servicecomb.config.common.ConfigConverter; +import org.apache.servicecomb.config.kie.client.KieClient; +import org.apache.servicecomb.config.kie.client.KieConfigManager; +import org.apache.servicecomb.config.kie.client.KieConfigurationChangedEvent; +import org.apache.servicecomb.config.kie.client.model.KieAddressManager; +import org.apache.servicecomb.config.kie.client.model.KieConfiguration; +import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; +import org.apache.servicecomb.http.client.common.HttpTransport; +import org.apache.servicecomb.http.client.common.HttpTransportFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +import com.google.common.eventbus.Subscribe; + +public class KieDynamicPropertiesSource implements DynamicPropertiesSource { + private static final Logger LOGGER = LoggerFactory.getLogger(KieDynamicPropertiesSource.class); + + public static final String SOURCE_NAME = "kie"; + + private final Map data = new ConcurrentHashMapEx<>(); + + private KieConfigManager kieConfigManager; + + private ConfigConverter configConverter; + + public KieDynamicPropertiesSource() { + + } + + private void init(Environment environment) { + KieConfig kieConfig = new KieConfig(environment); + configConverter = new ConfigConverter(kieConfig.getFileSources()); + KieAddressManager kieAddressManager = configKieAddressManager(kieConfig, environment); + + RequestConfig.Builder requestBuilder = buildRequestConfigBuilder(kieConfig); + if (kieConfig.enableLongPolling() + && kieConfig.getPollingWaitTime() >= 0) { + requestBuilder.setConnectionRequestTimeout(kieConfig.getPollingWaitTime() * 2 * 1000); + requestBuilder.setSocketTimeout(kieConfig.getPollingWaitTime() * 2 * 1000); + } + HttpTransport httpTransport = createHttpTransport(kieAddressManager, requestBuilder.build(), + kieConfig, environment); + KieConfiguration kieConfiguration = createKieConfiguration(kieConfig, environment); + KieClient kieClient = new KieClient(kieAddressManager, httpTransport, kieConfiguration); + EventManager.register(this); + kieConfigManager = new KieConfigManager(kieClient, EventManager.getEventBus(), kieConfiguration, configConverter, + kieAddressManager); + kieConfigManager.firstPull(); + kieConfigManager.startConfigKieManager(); + data.putAll(configConverter.getCurrentData()); + } + + private Builder buildRequestConfigBuilder(KieConfig kieConfig) { + Builder builder = HttpTransportFactory.defaultRequestConfig(); + builder.setConnectTimeout(kieConfig.getConnectTimeout()); + builder.setConnectionRequestTimeout(kieConfig.getConnectionRequestTimeout()); + builder.setSocketTimeout(kieConfig.getSocketTimeout()); + return builder; + } + + @Subscribe + public void onConfigurationChangedEvent(KieConfigurationChangedEvent event) { + LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); + data.putAll(event.getAdded()); + data.putAll(event.getUpdated()); + event.getDeleted().forEach((k, v) -> data.remove(k)); + EventManager.post(ConfigurationChangedEvent.createIncremental(event.getAdded(), + event.getUpdated(), event.getDeleted())); + } + + private KieConfiguration createKieConfiguration(KieConfig kieConfig, Environment environment) { + return new KieConfiguration() + .setAppName(BootStrapProperties.readApplication(environment)) + .setServiceName(BootStrapProperties.readServiceName(environment)) + .setEnvironment(BootStrapProperties.readServiceEnvironment(environment)) + .setVersion(BootStrapProperties.readServiceVersion(environment)) + .setFirstPullRequired(kieConfig.firstPullRequired()) + .setCustomLabel(kieConfig.getCustomLabel()) + .setCustomLabelValue(kieConfig.getCustomLabelValue()) + .setEnableAppConfig(kieConfig.enableAppConfig()) + .setEnableCustomConfig(kieConfig.enableCustomConfig()) + .setEnableLongPolling(kieConfig.enableLongPolling()) + .setEnableServiceConfig(kieConfig.enableServiceConfig()) + .setEnableVersionConfig(kieConfig.enableVersionConfig()) + .setPollingWaitInSeconds(kieConfig.getPollingWaitTime()) + .setProject(kieConfig.getDomainName()) + .setRefreshIntervalInMillis(kieConfig.getRefreshInterval()); + } + + private HttpTransport createHttpTransport(KieAddressManager kieAddressManager, + RequestConfig requestConfig, KieConfig kieConfig, + Environment environment) { + List authHeaderProviders = SPIServiceUtils.getOrLoadSortedService(AuthHeaderProvider.class); + + if (kieConfig.isProxyEnable()) { + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). + setDefaultRequestConfig(requestConfig); + HttpHost proxy = new HttpHost(kieConfig.getProxyHost(), + kieConfig.getProxyPort(), "http"); // now only support http proxy + httpClientBuilder.setProxy(proxy); + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(new AuthScope(proxy), + new UsernamePasswordCredentials(kieConfig.getProxyUsername(), + kieConfig.getProxyPasswd())); + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + + return HttpTransportFactory + .createHttpTransport( + TransportUtils + .createSSLProperties(kieAddressManager.sslEnabled(), environment, KieConfig.SSL_TAG), + getRequestAuthHeaderProvider(authHeaderProviders), httpClientBuilder); + } + + return HttpTransportFactory + .createHttpTransport( + TransportUtils + .createSSLProperties(kieAddressManager.sslEnabled(), environment, KieConfig.SSL_TAG), + getRequestAuthHeaderProvider(authHeaderProviders), requestConfig); + } + + private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List authHeaderProviders) { + return signRequest -> { + String host = signRequest != null && signRequest.getEndpoint() != null ? signRequest.getEndpoint().getHost() : ""; + Map headers = new HashMap<>(); + authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders(host))); + return headers; + }; + } + + private KieAddressManager configKieAddressManager(KieConfig kieConfig, Environment environment) { + String region = environment.getProperty("servicecomb.datacenter.region"); + String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); + return new KieAddressManager( + Arrays.asList(kieConfig.getServerUri().split(",")), EventManager.getEventBus(), region, availableZone); + } + + @Override + public PropertySource create(Environment environment) { + init(environment); + return new MapPropertySource(SOURCE_NAME, data); + } + + @Override + public int getOrder() { + return 0; + } +} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/TransportUtils.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/TransportUtils.java index 56afa18ed3b..8be7ec00c1a 100644 --- a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/TransportUtils.java +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/TransportUtils.java @@ -19,13 +19,13 @@ import static org.apache.servicecomb.foundation.ssl.SSLOption.DEFAULT_OPTION; -import org.apache.commons.configuration.Configuration; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; +import org.springframework.core.env.Environment; public class TransportUtils { - public static SSLProperties createSSLProperties(boolean sslEnabled, Configuration configuration, String tag) { + public static SSLProperties createSSLProperties(boolean sslEnabled, Environment environment, String tag) { SSLProperties sslProperties = new SSLProperties(); sslProperties.setEnabled(sslEnabled); @@ -34,93 +34,93 @@ public static SSLProperties createSSLProperties(boolean sslEnabled, Configuratio } SSLOption option = new SSLOption(); - option.setEngine(getStringProperty(configuration, + option.setEngine(getStringProperty(environment, DEFAULT_OPTION.getEngine(), "ssl." + tag + ".engine", "ssl.engine")); option.setProtocols( - getStringProperty(configuration, + getStringProperty(environment, DEFAULT_OPTION.getProtocols(), "ssl." + tag + ".protocols", "ssl.protocols")); option.setCiphers( - getStringProperty(configuration, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers")); + getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers")); option.setAuthPeer( - getBooleanProperty(configuration, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer")); + getBooleanProperty(environment, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer")); option.setCheckCNHost( - getBooleanProperty(configuration, + getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNHost(), "ssl." + tag + ".checkCN.host", "ssl.checkCN.host")); option.setCheckCNWhite( - getBooleanProperty(configuration, + getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNWhite(), "ssl." + tag + ".checkCN.white", "ssl.checkCN.white")); - option.setCheckCNWhiteFile(getStringProperty(configuration, + option.setCheckCNWhiteFile(getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".checkCN.white.file", "ssl.checkCN.white.file")); - option.setAllowRenegociate(getBooleanProperty(configuration, - DEFAULT_OPTION.isAllowRenegociate(), - "ssl." + tag + ".allowRenegociate", - "ssl.allowRenegociate")); + option.setAllowRenegotiate(getBooleanProperty(environment, + DEFAULT_OPTION.isAllowRenegotiate(), + "ssl." + tag + ".allowRenegotiate", + "ssl.allowRenegotiate")); option.setStorePath( - getStringProperty(configuration, + getStringProperty(environment, DEFAULT_OPTION.getStorePath(), "ssl." + tag + ".storePath", "ssl.storePath")); option.setClientAuth( - getStringProperty(configuration, + getStringProperty(environment, DEFAULT_OPTION.getClientAuth(), "ssl." + tag + ".clientAuth", "ssl.clientAuth")); option.setTrustStore( - getStringProperty(configuration, + getStringProperty(environment, DEFAULT_OPTION.getTrustStore(), "ssl." + tag + ".trustStore", "ssl.trustStore")); - option.setTrustStoreType(getStringProperty(configuration, + option.setTrustStoreType(getStringProperty(environment, DEFAULT_OPTION.getTrustStoreType(), "ssl." + tag + ".trustStoreType", "ssl.trustStoreType")); - option.setTrustStoreValue(getStringProperty(configuration, + option.setTrustStoreValue(getStringProperty(environment, DEFAULT_OPTION.getTrustStoreValue(), "ssl." + tag + ".trustStoreValue", "ssl.trustStoreValue")); option.setKeyStore( - getStringProperty(configuration, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore")); + getStringProperty(environment, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore")); option.setKeyStoreType( - getStringProperty(configuration, + getStringProperty(environment, DEFAULT_OPTION.getKeyStoreType(), "ssl." + tag + ".keyStoreType", "ssl.keyStoreType")); - option.setKeyStoreValue(getStringProperty(configuration, + option.setKeyStoreValue(getStringProperty(environment, DEFAULT_OPTION.getKeyStoreValue(), "ssl." + tag + ".keyStoreValue", "ssl.keyStoreValue")); - option.setCrl(getStringProperty(configuration, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl")); + option.setCrl(getStringProperty(environment, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl")); option.setSslCustomClass( - getStringProperty(configuration, null, "ssl." + tag + ".sslCustomClass", "ssl.sslCustomClass")); + getStringProperty(environment, null, "ssl." + tag + ".sslCustomClass", "ssl.sslCustomClass")); sslProperties.setSslOption(option); sslProperties.setSslCustom(SSLCustom.createSSLCustom(option.getSslCustomClass())); return sslProperties; } - private static String getStringProperty(Configuration configuration, String defaultValue, String... keys) { + private static String getStringProperty(Environment environment, String defaultValue, String... keys) { for (String key : keys) { - if (configuration.containsKey(key)) { - return configuration.getString(key); + if (environment.getProperty(key) != null) { + return environment.getProperty(key); } } return defaultValue; } - private static boolean getBooleanProperty(Configuration configuration, boolean defaultValue, String... keys) { + private static boolean getBooleanProperty(Environment environment, boolean defaultValue, String... keys) { for (String key : keys) { - if (configuration.containsKey(key)) { - return configuration.getBoolean(key); + if (environment.getProperty(key) != null) { + return environment.getProperty(key, boolean.class, false); } } return defaultValue; diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/collect/KieCenterDefaultDeploymentProvider.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/collect/KieCenterDefaultDeploymentProvider.java deleted file mode 100644 index 9412314d42e..00000000000 --- a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/collect/KieCenterDefaultDeploymentProvider.java +++ /dev/null @@ -1,53 +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.servicecomb.config.kie.collect; - -import java.util.List; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.deployment.DeploymentProvider; -import org.apache.servicecomb.deployment.SystemBootstrapInfo; - -import com.google.common.annotations.VisibleForTesting; - -public class KieCenterDefaultDeploymentProvider implements DeploymentProvider { - public static final String SYSTEM_KEY_KIE_CENTER = "KieCenter"; - - private static AbstractConfiguration configuration = ConfigUtil.createLocalConfig(); - - @Override - public SystemBootstrapInfo getSystemBootStrapInfo(String systemKey) { - if (!systemKey.equals(SYSTEM_KEY_KIE_CENTER)) { - return null; - } - List kieAddresses = ConfigUtil - .parseArrayValue(configuration.getString("servicecomb.kie.serverUri")); - if (kieAddresses.isEmpty()) { - return null; - } - SystemBootstrapInfo kie = new SystemBootstrapInfo(); - kie.setAccessURL(kieAddresses); - return kie; - } - - @VisibleForTesting - public static void setConfiguration(AbstractConfiguration configuration) { - KieCenterDefaultDeploymentProvider.configuration = configuration; - } -} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/collect/KieClientInformationCollector.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/collect/KieClientInformationCollector.java index eed0c9d4c52..d689aaec2e1 100644 --- a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/collect/KieClientInformationCollector.java +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/collect/KieClientInformationCollector.java @@ -17,25 +17,27 @@ package org.apache.servicecomb.config.kie.collect; +import org.apache.servicecomb.config.kie.KieConfig; import org.apache.servicecomb.core.bootup.BootUpInformationCollector; -import org.apache.servicecomb.deployment.Deployment; -import org.apache.servicecomb.deployment.SystemBootstrapInfo; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; +import org.springframework.lang.NonNull; -import java.util.Objects; +public class KieClientInformationCollector implements BootUpInformationCollector, EnvironmentAware { + private KieConfig kieConfig; + @Override + public void setEnvironment(@NonNull Environment environment) { + this.kieConfig = new KieConfig(environment); + } -public class KieClientInformationCollector implements BootUpInformationCollector { @Override public String collect() { - return "Kie Center: " + getCenterInfo(Deployment.getSystemBootStrapInfo("KieCenter")); + return "Kie Center: " + kieConfig.getServerUri(); } @Override public int getOrder() { return 2; } - - private String getCenterInfo(SystemBootstrapInfo systemBootstrapInfo) { - return Objects.isNull(systemBootstrapInfo) ? "not exist" : systemBootstrapInfo.getAccessURL().toString(); - } } diff --git a/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource b/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource new file mode 100644 index 00000000000..1ff1d0d0bb4 --- /dev/null +++ b/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource @@ -0,0 +1,18 @@ +# +# 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.servicecomb.config.kie.KieDynamicPropertiesSource diff --git a/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource b/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource deleted file mode 100644 index 67520d4dc32..00000000000 --- a/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.config.kie.KieConfigurationSourceImpl \ No newline at end of file diff --git a/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector b/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector index cb357c9c3f8..4ff2984a1b2 100644 --- a/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector +++ b/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -org.apache.servicecomb.config.kie.collect.KieClientInformationCollector \ No newline at end of file +org.apache.servicecomb.config.kie.collect.KieClientInformationCollector diff --git a/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.deployment.DeploymentProvider b/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.deployment.DeploymentProvider deleted file mode 100644 index c712d01e397..00000000000 --- a/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.deployment.DeploymentProvider +++ /dev/null @@ -1,17 +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. -# -org.apache.servicecomb.config.kie.collect.KieCenterDefaultDeploymentProvider \ No newline at end of file diff --git a/dynamic-config/config-kie/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/dynamic-config/config-kie/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..347d30ccef4 --- /dev/null +++ b/dynamic-config/config-kie/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.config.kie.KieConfigConfiguration diff --git a/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/collect/TestKieCenterDefaultDeploymentProvider.java b/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/collect/TestKieCenterDefaultDeploymentProvider.java deleted file mode 100644 index 805336c2386..00000000000 --- a/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/collect/TestKieCenterDefaultDeploymentProvider.java +++ /dev/null @@ -1,69 +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.servicecomb.config.kie.collect; - -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.deployment.Deployment; -import org.apache.servicecomb.deployment.SystemBootstrapInfo; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class TestKieCenterDefaultDeploymentProvider { - @BeforeEach - public void start() { - ArchaiusUtils.resetConfig(); - } - - @AfterEach - public void tearDown() { - ArchaiusUtils.resetConfig(); - } - - @Test - public void testConfiguration() { - KieCenterDefaultDeploymentProvider.setConfiguration(ConfigUtil.createLocalConfig()); - SystemBootstrapInfo info = Deployment.getSystemBootStrapInfo(KieCenterDefaultDeploymentProvider.SYSTEM_KEY_KIE_CENTER); - Assertions.assertEquals("https://172.16.8.7:30110", info.getAccessURL().get(0)); - Assertions.assertNull(Deployment.getSystemBootStrapInfo("wrong")); - } - - @Test - public void testConfigurationEnv() { - System.setProperty("servicecomb.kie.serverUri", "https://localhost:30110"); - KieCenterDefaultDeploymentProvider.setConfiguration(ConfigUtil.createLocalConfig()); - SystemBootstrapInfo info = Deployment.getSystemBootStrapInfo(KieCenterDefaultDeploymentProvider.SYSTEM_KEY_KIE_CENTER); - Assertions.assertEquals("https://localhost:30110", info.getAccessURL().get(0)); - System.getProperties().remove("servicecomb.kie.serverUri"); - } - - @Test - public void testConfigurationEnvTwo() { - System.setProperty("servicecomb.kie.serverUri", "http://127.0.0.1:30110,http://127.0.0.2:30110"); - KieCenterDefaultDeploymentProvider.setConfiguration(ConfigUtil.createLocalConfig()); - - SystemBootstrapInfo info = Deployment.getSystemBootStrapInfo(KieCenterDefaultDeploymentProvider.SYSTEM_KEY_KIE_CENTER); - Assertions.assertEquals(2, info.getAccessURL().size()); - Assertions.assertEquals("http://127.0.0.1:30110", info.getAccessURL().get(0)); - Assertions.assertEquals("http://127.0.0.2:30110", info.getAccessURL().get(1)); - - System.getProperties().remove("servicecomb.kie.serverUri"); - } -} diff --git a/dynamic-config/config-nacos/README.md b/dynamic-config/config-nacos/README.md index 8e817fc043a..e09d8db761d 100644 --- a/dynamic-config/config-nacos/README.md +++ b/dynamic-config/config-nacos/README.md @@ -1,5 +1,4 @@ # Dynamic configurations implementation with [Nacos](https://github.com/alibaba/nacos) Read [developers guide](https://docs.servicecomb.io/java-chassis/zh_CN/config/general-config/) -for details. - +for details. diff --git a/dynamic-config/config-nacos/pom.xml b/dynamic-config/config-nacos/pom.xml index a30caa4e6c1..395603f9fd6 100644 --- a/dynamic-config/config-nacos/pom.xml +++ b/dynamic-config/config-nacos/pom.xml @@ -22,7 +22,7 @@ dynamic-config org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 config-nacos diff --git a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosClient.java b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosClient.java new file mode 100644 index 00000000000..de9527b17ab --- /dev/null +++ b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosClient.java @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config.nacos; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.nacos.NacosDynamicPropertiesSource.UpdateHandler; +import org.apache.servicecomb.config.parser.Parser; +import org.springframework.core.env.Environment; + +import com.alibaba.nacos.api.NacosFactory; +import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.config.listener.Listener; +import com.alibaba.nacos.api.exception.NacosException; + +public class NacosClient { + private final UpdateHandler updateHandler; + + private final NacosConfig nacosConfig; + + private final Environment environment; + + private final Object lock = new Object(); + + private Map application = new HashMap<>(); + + private Map service = new HashMap<>(); + + private Map version = new HashMap<>(); + + private Map profile = new HashMap<>(); + + private Map custom = new HashMap<>(); + + private Map allLast = new HashMap<>(); + + public NacosClient(UpdateHandler updateHandler, Environment environment) { + this.updateHandler = updateHandler; + this.nacosConfig = new NacosConfig(environment); + this.environment = environment; + } + + public void refreshNacosConfig() throws NacosException { + Properties properties = nacosProperties(environment, nacosConfig); + + ConfigService configService = NacosFactory.createConfigService(properties); + addApplicationConfig(configService); + addServiceConfig(configService); + addVersionConfig(configService); + addProfileConfig(configService); + addCustomConfig(configService); + + refreshConfigItems(); + } + + private void addApplicationConfig(ConfigService configService) throws NacosException { + String content = configService.getConfig(BootStrapProperties.readApplication(environment), + BootStrapProperties.readApplication(environment), 5000); + processApplicationConfig(content); + configService.addListener(BootStrapProperties.readApplication(environment), + BootStrapProperties.readApplication(environment), new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + processApplicationConfig(configInfo); + refreshConfigItems(); + } + + @Override + public Executor getExecutor() { + return null; + } + }); + } + + private void processApplicationConfig(String content) { + if (StringUtils.isEmpty(content)) { + this.application = new HashMap<>(); + return; + } + Parser contentParser = Parser.findParser("yaml"); + this.application = contentParser.parse(content, "", false); + } + + private void addServiceConfig(ConfigService configService) throws NacosException { + String content = configService.getConfig(BootStrapProperties.readServiceName(environment), + BootStrapProperties.readApplication(environment), + 5000); + processServiceConfig(content); + configService.addListener(BootStrapProperties.readServiceName(environment), + BootStrapProperties.readApplication(environment), new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + processServiceConfig(configInfo); + refreshConfigItems(); + } + + @Override + public Executor getExecutor() { + return null; + } + }); + } + + private void processServiceConfig(String content) { + if (StringUtils.isEmpty(content)) { + this.service = new HashMap<>(); + return; + } + Parser contentParser = Parser.findParser("yaml"); + this.service = contentParser.parse(content, "", false); + } + + private void addVersionConfig(ConfigService configService) throws NacosException { + String content = configService.getConfig( + BootStrapProperties.readServiceName(environment) + "-" + + BootStrapProperties.readServiceVersion(environment), + BootStrapProperties.readApplication(environment), + 5000); + processVersionConfig(content); + configService.addListener(BootStrapProperties.readServiceName(environment) + "-" + + BootStrapProperties.readServiceVersion(environment), + BootStrapProperties.readApplication(environment), new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + processVersionConfig(configInfo); + refreshConfigItems(); + } + + @Override + public Executor getExecutor() { + return null; + } + }); + } + + private void processVersionConfig(String content) { + if (StringUtils.isEmpty(content)) { + this.version = new HashMap<>(); + return; + } + Parser contentParser = Parser.findParser("yaml"); + this.version = contentParser.parse(content, "", false); + } + + private void addProfileConfig(ConfigService configService) throws NacosException { + String profile = environment.getProperty("spring.profiles.active"); + if (StringUtils.isEmpty(profile)) { + return; + } + String content = configService.getConfig(BootStrapProperties.readServiceName(environment) + "-" + profile, + BootStrapProperties.readApplication(environment), 5000); + processProfileConfig(content); + configService.addListener(BootStrapProperties.readServiceName(environment) + "-" + profile, + BootStrapProperties.readApplication(environment), new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + processProfileConfig(configInfo); + refreshConfigItems(); + } + + @Override + public Executor getExecutor() { + return null; + } + }); + } + + private void processProfileConfig(String content) { + if (StringUtils.isEmpty(content)) { + this.profile = new HashMap<>(); + return; + } + Parser contentParser = Parser.findParser("yaml"); + this.profile = contentParser.parse(content, "", false); + } + + private void addCustomConfig(ConfigService configService) throws NacosException { + if (StringUtils.isEmpty(nacosConfig.getDataId()) || StringUtils.isEmpty(nacosConfig.getGroup())) { + return; + } + String content = configService.getConfig(nacosConfig.getDataId(), + nacosConfig.getGroup(), 5000); + processCustomConfig(content); + configService.addListener(nacosConfig.getDataId(), + nacosConfig.getGroup(), new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + processCustomConfig(configInfo); + refreshConfigItems(); + } + + @Override + public Executor getExecutor() { + return null; + } + }); + } + + private void processCustomConfig(String content) { + if (StringUtils.isEmpty(content)) { + this.custom = new HashMap<>(); + return; + } + Parser contentParser = Parser.findParser(nacosConfig.getContentType()); + String keyPrefix = nacosConfig.getGroup() + "." + + nacosConfig.getDataId(); + this.custom = contentParser.parse(content, keyPrefix, nacosConfig.getAddPrefix()); + } + + private void refreshConfigItems() { + synchronized (lock) { + Map all = new HashMap<>(); + all.putAll(application); + all.putAll(service); + all.putAll(version); + all.putAll(profile); + all.putAll(custom); + updateHandler.handle(all, allLast); + this.allLast = all; + } + } + + private static Properties nacosProperties(Environment environment, NacosConfig nacosConfig) { + Properties properties = new Properties(); + properties.put(NacosConfig.PROP_NAMESPACE, BootStrapProperties.readServiceEnvironment(environment)); + properties.put(NacosConfig.PROP_ADDRESS, nacosConfig.getServerAddr()); + if (nacosConfig.getUsername() != null) { + properties.put(NacosConfig.PROP_USERNAME, nacosConfig.getUsername()); + } + if (nacosConfig.getPassword() != null) { + properties.put(NacosConfig.PROP_PASSWORD, nacosConfig.getPassword()); + } + if (nacosConfig.getAccessKey() != null) { + properties.put(NacosConfig.PROP_ACCESS_KEY, nacosConfig.getAccessKey()); + } + if (nacosConfig.getSecretKey() != null) { + properties.put(NacosConfig.PROP_SECRET_KEY, nacosConfig.getSecretKey()); + } + return properties; + } +} diff --git a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosConfig.java b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosConfig.java new file mode 100644 index 00000000000..8963bd75c2d --- /dev/null +++ b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosConfig.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config.nacos; + +import org.springframework.core.env.Environment; + +public class NacosConfig { + public static final String PROPERTY_DATA_ID = "servicecomb.nacos.dataId"; + + public static final String PROPERTY_SERVER_ADDR = "servicecomb.nacos.serverAddr"; + + public static final String PROPERTY_GROUP = "servicecomb.nacos.group"; + + public static final String PROPERTY_ADD_PREFIX = "servicecomb.nacos.addPrefix"; + + public static final String PROPERTY_CONTENT_TYPE = "servicecomb.nacos.contentType"; + + public static final String PROPERTY_USERNAME = "servicecomb.nacos.username"; + + public static final String PROPERTY_PASSWORD = "servicecomb.nacos.password"; + + public static final String PROPERTY_ACCESS_KEY = "servicecomb.nacos.accessKey"; + + public static final String PROPERTY_SECRET_KEY = "servicecomb.nacos.secretKey"; + + public static final String PROP_NAMESPACE = "namespace"; + + public static final String PROP_ADDRESS = "serverAddr"; + + public static final String PROP_USERNAME = "username"; + + public static final String PROP_PASSWORD = "password"; + + public static final String PROP_ACCESS_KEY = "accessKey"; + + public static final String PROP_SECRET_KEY = "secretKey"; + + private final Environment environment; + + public NacosConfig(Environment environment) { + this.environment = environment; + } + + public String getServerAddr() { + return environment.getProperty(PROPERTY_SERVER_ADDR, "http://127.0.0.1:8848"); + } + + public String getDataId() { + return environment.getProperty(PROPERTY_DATA_ID); + } + + public String getGroup() { + return environment.getProperty(PROPERTY_GROUP); + } + + public String getUsername() { + return environment.getProperty(PROPERTY_USERNAME); + } + + public String getPassword() { + return environment.getProperty(PROPERTY_PASSWORD); + } + + public String getAccessKey() { + return environment.getProperty(PROPERTY_ACCESS_KEY); + } + + public String getSecretKey() { + return environment.getProperty(PROPERTY_SECRET_KEY); + } + + public String getContentType() { + return environment.getProperty(PROPERTY_CONTENT_TYPE, "yaml"); + } + + public boolean getAddPrefix() { + return environment.getProperty(PROPERTY_ADD_PREFIX, boolean.class, false); + } +} diff --git a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosDynamicPropertiesSource.java b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosDynamicPropertiesSource.java new file mode 100644 index 00000000000..308b6337615 --- /dev/null +++ b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosDynamicPropertiesSource.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.nacos; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.config.DynamicPropertiesSource; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +public class NacosDynamicPropertiesSource implements DynamicPropertiesSource { + public static final String SOURCE_NAME = "nacos"; + + private static final Logger LOGGER = LoggerFactory.getLogger(NacosDynamicPropertiesSource.class); + + private final Map valueCache = new ConcurrentHashMap<>(); + + public NacosDynamicPropertiesSource() { + } + + private final UpdateHandler updateHandler = new UpdateHandler(); + + private void init(Environment environment) { + NacosClient nacosClient = new NacosClient(updateHandler, environment); + try { + nacosClient.refreshNacosConfig(); + } catch (Exception e) { + throw new IllegalStateException("Set up nacos config failed.", e); + } + } + + public class UpdateHandler { + public void handle(Map current, Map last) { + ConfigurationChangedEvent event = ConfigurationChangedEvent.createIncremental(current, last); + LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); + valueCache.putAll(event.getAdded()); + valueCache.putAll(event.getUpdated()); + event.getDeleted().forEach((k, v) -> valueCache.remove(k)); + EventManager.post(event); + } + } + + @Override + public PropertySource create(Environment environment) { + init(environment); + return new MapPropertySource(SOURCE_NAME, valueCache); + } + + @Override + public int getOrder() { + return 0; + } +} diff --git a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/archaius/sources/NacosConfigurationSourceImpl.java b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/archaius/sources/NacosConfigurationSourceImpl.java deleted file mode 100644 index 528553c1e3c..00000000000 --- a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/archaius/sources/NacosConfigurationSourceImpl.java +++ /dev/null @@ -1,145 +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.servicecomb.config.nacos.archaius.sources; - -import static com.netflix.config.WatchedUpdateResult.createIncremental; -import static org.apache.servicecomb.config.nacos.client.ConfigurationAction.CREATE; -import static org.apache.servicecomb.config.nacos.client.ConfigurationAction.DELETE; -import static org.apache.servicecomb.config.nacos.client.ConfigurationAction.SET; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.configuration.Configuration; -import org.apache.servicecomb.config.ConfigMapping; -import org.apache.servicecomb.config.nacos.client.ConfigurationAction; -import org.apache.servicecomb.config.nacos.client.NacosClient; -import org.apache.servicecomb.config.nacos.client.NacosConfig; -import org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.ImmutableMap; -import com.netflix.config.WatchedUpdateListener; -import com.netflix.config.WatchedUpdateResult; - -public class NacosConfigurationSourceImpl implements ConfigCenterConfigurationSource { - private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCenterConfigurationSource.class); - - private final Map valueCache = new ConcurrentHashMap<>(); - - private final List listeners = new CopyOnWriteArrayList<>(); - - public NacosConfigurationSourceImpl() { - } - - private final UpdateHandler updateHandler = new UpdateHandler(); - - @VisibleForTesting - UpdateHandler getUpdateHandler() { - return updateHandler; - } - - @Override - public int getOrder() { - return ORDER_BASE * 4; - } - - @Override - public boolean isValidSource(Configuration localConfiguration) { - if (localConfiguration.getProperty(NacosConfig.SERVER_ADDR) == null) { - LOGGER.warn("Nacos configuration source is not configured!"); - return false; - } - return true; - } - - @Override - public void init(Configuration localConfiguration) { - NacosConfig.setConcurrentCompositeConfiguration(localConfiguration); - init(); - } - - private void init() { - NacosClient nacosClient = new NacosClient(updateHandler); - nacosClient.refreshNacosConfig(); - } - - @Override - public void addUpdateListener(WatchedUpdateListener watchedUpdateListener) { - listeners.add(watchedUpdateListener); - } - - @Override - public void removeUpdateListener(WatchedUpdateListener watchedUpdateListener) { - listeners.remove(watchedUpdateListener); - } - - private void updateConfiguration(WatchedUpdateResult result) { - for (WatchedUpdateListener l : listeners) { - try { - l.updateConfiguration(result); - } catch (Throwable ex) { - LOGGER.error("Error in invoking WatchedUpdateListener", ex); - } - } - } - - @Override - public Map getCurrentData() throws Exception { - return valueCache; - } - - public List getCurrentListeners() { - return listeners; - } - - public class UpdateHandler { - public void handle(ConfigurationAction action, Map config) { - if (config == null || config.isEmpty()) { - return; - } - Map configuration = ConfigMapping.getConvertedMap(config); - if (CREATE.equals(action)) { - valueCache.putAll(configuration); - - updateConfiguration(createIncremental(ImmutableMap.copyOf(configuration), - null, - null)); - } else if (SET.equals(action)) { - valueCache.putAll(configuration); - - updateConfiguration(createIncremental(null, - ImmutableMap.copyOf(configuration), - null)); - } else if (DELETE.equals(action)) { - configuration.keySet().forEach(valueCache::remove); - updateConfiguration(createIncremental(null, - null, - ImmutableMap.copyOf(configuration))); - } else { - LOGGER.error("action: {} is invalid.", action.name()); - return; - } - LOGGER.warn("Config value cache changed: action:{}; item:{}", action.name(), configuration.keySet()); - } - } -} diff --git a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/client/ConfigurationAction.java b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/client/ConfigurationAction.java deleted file mode 100644 index 3defe34f076..00000000000 --- a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/client/ConfigurationAction.java +++ /dev/null @@ -1,24 +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.servicecomb.config.nacos.client; - -public enum ConfigurationAction { - CREATE, - SET, - DELETE -} diff --git a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/client/NacosClient.java b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/client/NacosClient.java deleted file mode 100644 index 6e02f492c74..00000000000 --- a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/client/NacosClient.java +++ /dev/null @@ -1,138 +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.servicecomb.config.nacos.client; - -import static org.apache.servicecomb.config.nacos.client.ConfigurationAction.CREATE; -import static org.apache.servicecomb.config.nacos.client.ConfigurationAction.DELETE; -import static org.apache.servicecomb.config.nacos.client.ConfigurationAction.SET; - -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; - -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.config.nacos.archaius.sources.NacosConfigurationSourceImpl.UpdateHandler; -import org.apache.servicecomb.config.parser.Parser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.alibaba.nacos.api.NacosFactory; -import com.alibaba.nacos.api.config.ConfigService; -import com.alibaba.nacos.api.config.listener.Listener; - -public class NacosClient { - - private static final Logger LOGGER = LoggerFactory.getLogger(NacosClient.class); - - private static final Map originalConfigMap = new ConcurrentHashMap<>(); - - private final UpdateHandler updateHandler; - - public NacosClient(UpdateHandler updateHandler) { - this.updateHandler = updateHandler; - } - - public void refreshNacosConfig() { - new ConfigRefresh().refreshConfig(); - } - - class ConfigRefresh { - Parser contentParser = Parser.findParser(NacosConfig.INSTANCE.getContentType()); - - String keyPrefix = NacosConfig.INSTANCE.getGroup() + "." + - NacosConfig.INSTANCE.getDataId(); - - ConfigRefresh() { - } - - @SuppressWarnings("unchecked") - void refreshConfig() { - Properties properties = new Properties(); - properties.put("serverAddr", NacosConfig.INSTANCE.getServerAddr()); - properties.put("namespace", NacosConfig.INSTANCE.getNameSpace()); - - try { - ConfigService configService = NacosFactory.createConfigService(properties); - String content = configService.getConfig(NacosConfig.INSTANCE.getDataId(), - NacosConfig.INSTANCE.getGroup(), 5000); - processContent(content); - configService.addListener(NacosConfig.INSTANCE.getDataId(), - NacosConfig.INSTANCE.getGroup(), new Listener() { - @Override - public void receiveConfigInfo(String configInfo) { - LOGGER.info("receive from nacos:" + configInfo); - processContent(configInfo); - } - - @Override - public Executor getExecutor() { - return null; - } - }); - } catch (Exception e) { - LOGGER.error("Receive nacos config error: ", e); - } - } - - private void processContent(String content) { - if (StringUtils.isEmpty(content)) { - return; - } - - refreshConfigItems(contentParser.parse(content, keyPrefix, NacosConfig.INSTANCE.getAddPrefix())); - } - - private void refreshConfigItems(Map map) { - compareChangedConfig(originalConfigMap, map); - originalConfigMap.clear(); - originalConfigMap.putAll(map); - } - - void compareChangedConfig(Map before, Map after) { - Map itemsCreated = new HashMap<>(); - Map itemsDeleted = new HashMap<>(); - Map itemsModified = new HashMap<>(); - if (before == null || before.isEmpty()) { - updateHandler.handle(CREATE, after); - return; - } - if (after == null || after.isEmpty()) { - updateHandler.handle(DELETE, before); - return; - } - after.forEach((itemKey, itemValue) -> { - if (!before.containsKey(itemKey)) { - itemsCreated.put(itemKey, itemValue); - } else if (!itemValue.equals(before.get(itemKey))) { - itemsModified.put(itemKey, itemValue); - } - }); - for (String itemKey : before.keySet()) { - if (!after.containsKey(itemKey)) { - itemsDeleted.put(itemKey, ""); - } - } - updateHandler.handle(CREATE, itemsCreated); - updateHandler.handle(SET, itemsModified); - updateHandler.handle(DELETE, itemsDeleted); - } - } -} - diff --git a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/client/NacosConfig.java b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/client/NacosConfig.java deleted file mode 100644 index 4fb31111ca6..00000000000 --- a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/client/NacosConfig.java +++ /dev/null @@ -1,69 +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.servicecomb.config.nacos.client; - -import org.apache.commons.configuration.Configuration; - -public class NacosConfig { - public static final NacosConfig INSTANCE = new NacosConfig(); - - private static Configuration finalConfig; - - public static final String DATA_ID = "servicecomb.nacos.dataId"; - - public static final String SERVER_ADDR = "servicecomb.nacos.serverAddr"; - - public static final String GROUP = "servicecomb.nacos.group"; - - public static final String ADD_PREFIX = "servicecomb.nacos.addPrefix"; - - public static final String NAME_SPACE = "servicecomb.nacos.namespace"; - - public static final String CONTENT_TYPE = "servicecomb.nacos.contentType"; - - private NacosConfig() { - } - - public static void setConcurrentCompositeConfiguration(Configuration config) { - finalConfig = config; - } - - public String getServerAddr() { - return finalConfig.getString(SERVER_ADDR); - } - - public String getDataId() { - return finalConfig.getString(DATA_ID); - } - - public String getGroup() { - return finalConfig.getString(GROUP); - } - - public String getNameSpace() { - return finalConfig.getString(NAME_SPACE, "public"); - } - - public String getContentType() { - return finalConfig.getString(CONTENT_TYPE, "yaml"); - } - - public boolean getAddPrefix() { - return finalConfig.getBoolean(ADD_PREFIX, true); - } -} diff --git a/dynamic-config/config-nacos/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource b/dynamic-config/config-nacos/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource new file mode 100644 index 00000000000..f7b7e102457 --- /dev/null +++ b/dynamic-config/config-nacos/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource @@ -0,0 +1,18 @@ +# +# 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.servicecomb.config.nacos.NacosDynamicPropertiesSource diff --git a/dynamic-config/config-nacos/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource b/dynamic-config/config-nacos/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource deleted file mode 100644 index 94965f14785..00000000000 --- a/dynamic-config/config-nacos/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.config.nacos.archaius.sources.NacosConfigurationSourceImpl diff --git a/dynamic-config/config-nacos/src/test/java/org/apache/servicecomb/config/nacos/archaius/sources/NacosConfigurationSourceImplTest.java b/dynamic-config/config-nacos/src/test/java/org/apache/servicecomb/config/nacos/archaius/sources/NacosConfigurationSourceImplTest.java deleted file mode 100644 index aa51be7d87b..00000000000 --- a/dynamic-config/config-nacos/src/test/java/org/apache/servicecomb/config/nacos/archaius/sources/NacosConfigurationSourceImplTest.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.servicecomb.config.nacos.archaius.sources; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.servicecomb.config.nacos.archaius.sources.NacosConfigurationSourceImpl.UpdateHandler; -import org.apache.servicecomb.config.nacos.client.ConfigurationAction; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import com.netflix.config.WatchedUpdateListener; - -public class NacosConfigurationSourceImplTest { - @Test - public void testCreate() throws Exception { - NacosConfigurationSourceImpl nacosConfigurationSource = new NacosConfigurationSourceImpl(); - nacosConfigurationSource.addUpdateListener(result -> Assertions.assertFalse(result.getAdded().isEmpty())); - UpdateHandler updateHandler = nacosConfigurationSource.getUpdateHandler(); - Map createItems = new HashMap<>(); - createItems.put("nacosTestKey", "testValue"); - updateHandler.handle(ConfigurationAction.CREATE, createItems); - } - - @Test - public void testUpdate() throws Exception { - NacosConfigurationSourceImpl nacosConfigurationSource = new NacosConfigurationSourceImpl(); - nacosConfigurationSource.addUpdateListener(result -> Assertions.assertFalse(result.getChanged().isEmpty())); - UpdateHandler updateHandler = nacosConfigurationSource.getUpdateHandler(); - Map updateItems = new HashMap<>(); - updateItems.put("nacosTestKey", "testValue"); - updateHandler.handle(ConfigurationAction.SET, updateItems); - - } - - @Test - public void testDelete() throws Exception { - NacosConfigurationSourceImpl nacosConfigurationSource = new NacosConfigurationSourceImpl(); - nacosConfigurationSource.addUpdateListener(result -> Assertions.assertFalse(result.getDeleted().isEmpty())); - UpdateHandler updateHandler = nacosConfigurationSource.getUpdateHandler(); - Map deleteItems = new HashMap<>(); - deleteItems.put("nacosTestKey", "testValue"); - - nacosConfigurationSource.getCurrentData().put("nacosTestKey", "testValue"); - updateHandler.handle(ConfigurationAction.DELETE, deleteItems); - Assertions.assertTrue(nacosConfigurationSource.getCurrentData().isEmpty()); - } - - @Test - public void testRemoveUpdateListener() { - NacosConfigurationSourceImpl nacosConfigurationSource = new NacosConfigurationSourceImpl(); - WatchedUpdateListener watchedUpdateListener = Mockito.mock(WatchedUpdateListener.class); - nacosConfigurationSource.addUpdateListener(watchedUpdateListener); - nacosConfigurationSource.removeUpdateListener(watchedUpdateListener); - Assertions.assertTrue(nacosConfigurationSource.getCurrentListeners().isEmpty()); - } -} diff --git a/dynamic-config/config-nacos/src/test/java/org/apache/servicecomb/config/nacos/client/NacosClientTest.java b/dynamic-config/config-nacos/src/test/java/org/apache/servicecomb/config/nacos/client/NacosClientTest.java deleted file mode 100644 index 3a49bbc4769..00000000000 --- a/dynamic-config/config-nacos/src/test/java/org/apache/servicecomb/config/nacos/client/NacosClientTest.java +++ /dev/null @@ -1,81 +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.servicecomb.config.nacos.client; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.config.nacos.archaius.sources.NacosConfigurationSourceImpl; -import org.apache.servicecomb.config.nacos.archaius.sources.NacosConfigurationSourceImpl.UpdateHandler; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class NacosClientTest { - @BeforeAll - public static void setUpClass() { - NacosConfig.setConcurrentCompositeConfiguration(ConfigUtil.createLocalConfig()); - } - - @Test - public void testCompareChangedConfig() { - boolean status = true; - Map before = new HashMap<>(); - Map after = new HashMap<>(); - - NacosConfigurationSourceImpl impl = new NacosConfigurationSourceImpl(); - UpdateHandler updateHandler = impl.new UpdateHandler(); - NacosClient nacosClient = new NacosClient(updateHandler); - - NacosClient.ConfigRefresh cr = nacosClient.new ConfigRefresh(); - - try { - cr.compareChangedConfig(before, after); - } catch (Exception e) { - status = false; - } - Assertions.assertTrue(status); - - status = true; - before.put("test", "testValue"); - try { - cr.compareChangedConfig(before, after); - } catch (Exception e) { - status = false; - } - Assertions.assertTrue(status); - - status = true; - after.put("test", "testValue2"); - try { - cr.compareChangedConfig(before, after); - } catch (Exception e) { - status = false; - } - Assertions.assertTrue(status); - - status = true; - try { - cr.compareChangedConfig(before, after); - } catch (Exception e) { - status = false; - } - Assertions.assertTrue(status); - } -} diff --git a/dynamic-config/config-nacos/src/test/java/org/apache/servicecomb/config/nacos/client/NacosConfigTest.java b/dynamic-config/config-nacos/src/test/java/org/apache/servicecomb/config/nacos/client/NacosConfigTest.java deleted file mode 100644 index 8b82d0d67e2..00000000000 --- a/dynamic-config/config-nacos/src/test/java/org/apache/servicecomb/config/nacos/client/NacosConfigTest.java +++ /dev/null @@ -1,38 +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.servicecomb.config.nacos.client; - -import org.apache.servicecomb.config.ConfigUtil; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class NacosConfigTest { - @BeforeAll - public static void setUpClass() { - NacosConfig.setConcurrentCompositeConfiguration(ConfigUtil.createLocalConfig()); - } - - @Test - public void getServiceName() { - NacosConfig instance = NacosConfig.INSTANCE; - Assertions.assertEquals("DEFAULT_GROUP", instance.getGroup()); - Assertions.assertEquals("127.0.0.1:8848", instance.getServerAddr()); - Assertions.assertEquals("example", instance.getDataId()); - } -} diff --git a/dynamic-config/config-zookeeper/pom.xml b/dynamic-config/config-zookeeper/pom.xml new file mode 100644 index 00000000000..35fabec9591 --- /dev/null +++ b/dynamic-config/config-zookeeper/pom.xml @@ -0,0 +1,49 @@ + + + + + + + dynamic-config + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + config-zookeeper + Java Chassis::Dynamic Config::Zookeeper + + + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-vertx + + + + org.apache.curator + curator-framework + + + org.apache.curator + curator-recipes + + + diff --git a/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperClient.java b/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperClient.java new file mode 100644 index 00000000000..407c577d4e5 --- /dev/null +++ b/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperClient.java @@ -0,0 +1,281 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.zookeeper; + +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; + +import org.apache.commons.lang3.StringUtils; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.recipes.cache.CuratorCache; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.zookeeper.ZookeeperDynamicPropertiesSource.UpdateHandler; +import org.apache.zookeeper.server.auth.DigestLoginModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ByteArrayResource; + +public class ZookeeperClient { + static class ZookeeperSASLConfig extends Configuration { + AppConfigurationEntry entry; + + public ZookeeperSASLConfig(String username, + String password) { + Map options = new HashMap<>(); + options.put("username", username); + options.put("password", password); + this.entry = new AppConfigurationEntry( + DigestLoginModule.class.getName(), + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, + options + ); + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + AppConfigurationEntry[] array = new AppConfigurationEntry[1]; + array[0] = entry; + return array; + } + } + + private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperClient.class); + + public static final String PATH_ENVIRONMENT = "/servicecomb/config/environment/%s"; + + public static final String PATH_APPLICATION = "/servicecomb/config/application/%s/%s"; + + public static final String PATH_SERVICE = "/servicecomb/config/service/%s/%s/%s"; + + public static final String PATH_VERSION = "/servicecomb/config/version/%s/%s/%s/%s"; + + public static final String PATH_TAG = "/servicecomb/config/tag/%s/%s/%s/%s/%s"; + + private final UpdateHandler updateHandler; + + private final ZookeeperConfig zookeeperConfig; + + private final Environment environment; + + private final Object lock = new Object(); + + private Map environmentData = new HashMap<>(); + + private Map applicationData = new HashMap<>(); + + private Map serviceData = new HashMap<>(); + + private Map versionData = new HashMap<>(); + + private Map tagData = new HashMap<>(); + + private Map allLast = new HashMap<>(); + + + public ZookeeperClient(UpdateHandler updateHandler, Environment environment) { + this.updateHandler = updateHandler; + this.zookeeperConfig = new ZookeeperConfig(environment); + this.environment = environment; + } + + public void refreshZookeeperConfig() throws Exception { + CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() + .connectString(zookeeperConfig.getConnectString()) + .sessionTimeoutMs(zookeeperConfig.getSessionTimeoutMillis()) + .retryPolicy(new ExponentialBackoffRetry(1000, 3)); + String authSchema = zookeeperConfig.getAuthSchema(); + if (StringUtils.isNotEmpty(authSchema)) { + if (!"digest".equals(authSchema)) { + throw new IllegalStateException("Not supported schema now. " + authSchema); + } + if (zookeeperConfig.getAuthInfo() == null) { + throw new IllegalStateException("Auth info can not be empty. "); + } + + String[] authInfo = zookeeperConfig.getAuthInfo().split(":"); + Configuration.setConfiguration(new ZookeeperSASLConfig(authInfo[0], authInfo[1])); + } + CuratorFramework client = builder.build(); + client.start(); + + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = ZookeeperConfig.ZOOKEEPER_DEFAULT_ENVIRONMENT; + } + addEnvironmentConfig(env, client); + addApplicationConfig(env, client); + addServiceConfig(env, client); + addVersionConfig(env, client); + addTagConfig(env, client); + + refreshConfigItems(); + } + + private void addTagConfig(String env, CuratorFramework client) throws Exception { + if (StringUtils.isEmpty(zookeeperConfig.getInstanceTag())) { + return; + } + String path = String.format(PATH_TAG, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment), + zookeeperConfig.getInstanceTag()); + CuratorCache cache = CuratorCache.builder(client, path).build(); + cache.listenable().addListener((type, oldData, newData) -> { + try { + this.tagData = parseData(client, path); + refreshConfigItems(); + } catch (Exception e) { + LOGGER.error("process event failed", e); + } + }); + cache.start(); + this.tagData = parseData(client, path); + } + + private void addVersionConfig(String env, CuratorFramework client) throws Exception { + String path = String.format(PATH_VERSION, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment)); + CuratorCache cache = CuratorCache.builder(client, path).build(); + cache.listenable().addListener((type, oldData, newData) -> { + try { + this.versionData = parseData(client, path); + refreshConfigItems(); + } catch (Exception e) { + LOGGER.error("process event failed", e); + } + }); + cache.start(); + this.versionData = parseData(client, path); + } + + private void addServiceConfig(String env, CuratorFramework client) throws Exception { + String path = String.format(PATH_SERVICE, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment)); + CuratorCache cache = CuratorCache.builder(client, path).build(); + cache.listenable().addListener((type, oldData, newData) -> { + try { + this.serviceData = parseData(client, path); + refreshConfigItems(); + } catch (Exception e) { + LOGGER.error("process event failed", e); + } + }); + cache.start(); + this.serviceData = parseData(client, path); + } + + private void addApplicationConfig(String env, CuratorFramework client) throws Exception { + String path = String.format(PATH_APPLICATION, env, BootStrapProperties.readApplication(environment)); + CuratorCache cache = CuratorCache.builder(client, path).build(); + cache.listenable().addListener((type, oldData, newData) -> { + try { + this.applicationData = parseData(client, path); + refreshConfigItems(); + } catch (Exception e) { + LOGGER.error("process event failed", e); + } + }); + cache.start(); + this.applicationData = parseData(client, path); + } + + private void addEnvironmentConfig(String env, CuratorFramework client) throws Exception { + String path = String.format(PATH_ENVIRONMENT, env); + CuratorCache cache = CuratorCache.builder(client, path).build(); + cache.listenable().addListener((type, oldData, newData) -> { + try { + this.environmentData = parseData(client, path); + refreshConfigItems(); + } catch (Exception e) { + LOGGER.error("process event failed", e); + } + }); + cache.start(); + this.environmentData = parseData(client, path); + } + + private Map parseData(CuratorFramework client, String path) throws Exception { + Map values = new HashMap<>(); + + if (client.checkExists().forPath(path) != null) { + client.getChildren().forPath(path).stream().sorted().forEach(item -> { + try { + byte[] data = client.getData().forPath(path + "/" + item); + if (item.endsWith(".yaml") || item.endsWith(".yml")) { + YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); + yamlFactory.setResources(new ByteArrayResource(data)); + values.putAll(toMap(yamlFactory.getObject())); + } else if (item.endsWith(".properties")) { + Properties properties = new Properties(); + properties.load(new StringReader(new String(data, StandardCharsets.UTF_8))); + values.putAll(toMap(properties)); + } else { + values.put(item, new String(data, StandardCharsets.UTF_8)); + } + } catch (Exception e) { + LOGGER.error("", e); + } + }); + } + + return values; + } + + private void refreshConfigItems() { + synchronized (lock) { + Map all = new HashMap<>(); + all.putAll(environmentData); + all.putAll(applicationData); + all.putAll(serviceData); + all.putAll(versionData); + all.putAll(tagData); + updateHandler.handle(all, allLast); + this.allLast = all; + } + } + + @SuppressWarnings("unchecked") + private Map toMap(Properties properties) { + if (properties == null) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(); + Enumeration keys = (Enumeration) properties.propertyNames(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + Object value = properties.getProperty(key); + result.put(key, value); + } + return result; + } +} diff --git a/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperConfig.java b/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperConfig.java new file mode 100644 index 00000000000..307ef7f0d55 --- /dev/null +++ b/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperConfig.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.servicecomb.config.zookeeper; + +import org.springframework.core.env.Environment; + +public class ZookeeperConfig { + public static final String ZOOKEEPER_DEFAULT_ENVIRONMENT = "production"; + + public static final String PROPERTY_CONNECT_STRING = "servicecomb.config.zk.connect-string"; + + public static final String PROPERTY_SESSION_TIMEOUT = "servicecomb.config.zk.session-timeout-millis"; + + public static final String PROPERTY_CONNECTION_TIMEOUT = "servicecomb.config.zk.connection-timeout-mills"; + + public static final String PROPERTY_AUTH_SCHEMA = "servicecomb.config.zk.authentication-schema"; + + public static final String PROPERTY_AUTH_INFO = "servicecomb.config.zk.authentication-info"; + + public static final String PROPERTY_INSTANCE_TAG = "servicecomb.config.zk.instance-tag"; + + private final Environment environment; + + public ZookeeperConfig(Environment environment) { + this.environment = environment; + } + + public String getConnectString() { + return environment.getProperty(PROPERTY_CONNECT_STRING, "127.0.0.1:2181"); + } + + public int getSessionTimeoutMillis() { + return environment.getProperty(PROPERTY_SESSION_TIMEOUT, int.class, 60000); + } + + public int getConnectionTimeoutMillis() { + return environment.getProperty(PROPERTY_CONNECTION_TIMEOUT, int.class, 1000); + } + + public String getAuthSchema() { + return environment.getProperty(PROPERTY_AUTH_SCHEMA); + } + + public String getAuthInfo() { + return environment.getProperty(PROPERTY_AUTH_INFO); + } + + public String getInstanceTag() { + return environment.getProperty(PROPERTY_INSTANCE_TAG); + } +} diff --git a/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperDynamicPropertiesSource.java b/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperDynamicPropertiesSource.java new file mode 100644 index 00000000000..a971ea5845f --- /dev/null +++ b/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperDynamicPropertiesSource.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.zookeeper; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.config.DynamicPropertiesSource; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +public class ZookeeperDynamicPropertiesSource implements DynamicPropertiesSource { + public static final String SOURCE_NAME = "zookeeper"; + + private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperDynamicPropertiesSource.class); + + private final Map valueCache = new ConcurrentHashMap<>(); + + public ZookeeperDynamicPropertiesSource() { + } + + private final UpdateHandler updateHandler = new UpdateHandler(); + + private void init(Environment environment) { + ZookeeperClient zookeeperClient = new ZookeeperClient(updateHandler, environment); + try { + zookeeperClient.refreshZookeeperConfig(); + } catch (Exception e) { + throw new IllegalStateException("Set up zookeeper config failed.", e); + } + } + + public class UpdateHandler { + public void handle(Map current, Map last) { + ConfigurationChangedEvent event = ConfigurationChangedEvent.createIncremental(current, last); + LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); + valueCache.putAll(event.getAdded()); + valueCache.putAll(event.getUpdated()); + event.getDeleted().forEach((k, v) -> valueCache.remove(k)); + EventManager.post(event); + } + } + + @Override + public PropertySource create(Environment environment) { + init(environment); + return new MapPropertySource(SOURCE_NAME, valueCache); + } + + @Override + public int getOrder() { + return 0; + } +} diff --git a/dynamic-config/config-zookeeper/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource b/dynamic-config/config-zookeeper/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource new file mode 100644 index 00000000000..7b3b32c721d --- /dev/null +++ b/dynamic-config/config-zookeeper/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource @@ -0,0 +1,18 @@ +# +# 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.servicecomb.config.zookeeper.ZookeeperDynamicPropertiesSource diff --git a/dynamic-config/pom.xml b/dynamic-config/pom.xml index fac881fc0fb..d5777cd654c 100644 --- a/dynamic-config/pom.xml +++ b/dynamic-config/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default 4.0.0 @@ -36,6 +36,9 @@ config-cc config-nacos config-kie + config-zookeeper + config-etcd + config-consul diff --git a/edge/edge-core/pom.xml b/edge/edge-core/pom.xml index a194b334f82..0a75f8e33c6 100644 --- a/edge/edge-core/pom.xml +++ b/edge/edge-core/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT edge-core Java Chassis::Edge::Core diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/AbstractEdgeDispatcher.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/AbstractEdgeDispatcher.java index 689da102bd4..c29c066b800 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/AbstractEdgeDispatcher.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/AbstractEdgeDispatcher.java @@ -17,8 +17,8 @@ package org.apache.servicecomb.edge.core; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CommonHttpEdgeDispatcher.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CommonHttpEdgeDispatcher.java index fa97290fdde..0f7e59bb0d2 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CommonHttpEdgeDispatcher.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CommonHttpEdgeDispatcher.java @@ -20,7 +20,13 @@ import java.util.HashMap; import java.util.Map; +import org.apache.servicecomb.common.rest.route.URLMappedConfigurationItem; +import org.apache.servicecomb.common.rest.route.URLMappedConfigurationLoader; +import org.apache.servicecomb.common.rest.route.Utils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; @@ -31,17 +37,16 @@ import org.apache.servicecomb.loadbalance.LoadBalancer; import org.apache.servicecomb.loadbalance.RuleExt; import org.apache.servicecomb.loadbalance.ServiceCombServer; -import org.apache.servicecomb.loadbalance.filter.ServerDiscoveryFilter; -import org.apache.servicecomb.registry.RegistrationManager; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTree; import org.apache.servicecomb.transport.rest.client.Http2TransportHttpClientOptionsSPI; import org.apache.servicecomb.transport.rest.client.HttpTransportHttpClientOptionsSPI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.DynamicPropertyFactory; +import com.google.common.eventbus.Subscribe; import io.vertx.core.Future; import io.vertx.core.Handler; @@ -75,42 +80,64 @@ public class CommonHttpEdgeDispatcher extends AbstractEdgeDispatcher { private Map configurations = new HashMap<>(); - private DiscoveryTree discoveryTree; + private Environment environment; public CommonHttpEdgeDispatcher() { + + } + + // though this is an SPI, but add as beans. + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; if (this.enabled()) { loadConfigurations(); - discoveryTree = new DiscoveryTree(); - discoveryTree.addFilter(new ServerDiscoveryFilter()); } } + // Maybe future change to beans + protected DiscoveryTree getDiscoveryTree() { + return BeanUtils.getBean(DiscoveryTree.class); + } + + // Maybe future change to beans + protected Environment getEnvironment() { + return BeanUtils.getBean(Environment.class); + } + + // Maybe future change to beans + protected ExtensionsManager getExtensionsManager() { + return BeanUtils.getBean(ExtensionsManager.class); + } + @Override public int getOrder() { - return DynamicPropertyFactory.getInstance().getIntProperty(KEY_ORDER, 40_000).get(); + return LegacyPropertyFactory.getIntProperty(KEY_ORDER, 40_000); } @Override public boolean enabled() { - return DynamicPropertyFactory.getInstance().getBooleanProperty(KEY_ENABLED, false).get(); + return environment.getProperty(KEY_ENABLED, boolean.class, false); } @Override public void init(Router router) { - String pattern = DynamicPropertyFactory.getInstance().getStringProperty(KEY_PATTERN, PATTERN_ANY).get(); + String pattern = environment.getProperty(KEY_PATTERN, PATTERN_ANY); router.routeWithRegex(pattern).failureHandler(this::onFailure).handler(this::onRequest); } private void loadConfigurations() { - ConcurrentCompositeConfiguration config = (ConcurrentCompositeConfiguration) DynamicPropertyFactory - .getBackingConfigurationSource(); - configurations = URLMappedConfigurationLoader.loadConfigurations(config, KEY_MAPPING_PREFIX); - config.addConfigurationListener(event -> { - if (event.getPropertyName().startsWith(KEY_MAPPING_PREFIX)) { - LOG.info("Map rule have been changed. Reload configurations. Event=" + event.getType()); - configurations = URLMappedConfigurationLoader.loadConfigurations(config, KEY_MAPPING_PREFIX); + configurations = URLMappedConfigurationLoader.loadConfigurations(environment, KEY_MAPPING_PREFIX); + } + + @Subscribe + public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { + for (String changed : event.getChanged()) { + if (changed.startsWith(KEY_MAPPING_PREFIX)) { + loadConfigurations(); + break; } - }); + } } protected void onRequest(RoutingContext context) { @@ -134,8 +161,8 @@ public String getMicroserviceName() { } }; - LoadBalancer loadBalancer = getOrCreateLoadBalancer(invocation, configurationItem.getMicroserviceName(), - configurationItem.getVersionRule()); + LoadBalancer loadBalancer = getOrCreateLoadBalancer(invocation, configurationItem.getMicroserviceName() + ); ServiceCombServer server = loadBalancer.chooseServer(invocation); if (server == null) { LOG.warn("no available server for service {}", configurationItem.getMicroserviceName()); @@ -195,20 +222,19 @@ protected Handler responseHandler(RoutingContext routingContext) { return data -> routingContext.response().write(data); } - protected LoadBalancer getOrCreateLoadBalancer(Invocation invocation, String microserviceName, String versionRule) { + protected LoadBalancer getOrCreateLoadBalancer(Invocation invocation, String microserviceName) { DiscoveryContext context = new DiscoveryContext(); context.setInputParameters(invocation); - VersionedCache serversVersionedCache = discoveryTree.discovery(context, - RegistrationManager.INSTANCE.getMicroservice().getAppId(), - microserviceName, - versionRule); + VersionedCache serversVersionedCache = getDiscoveryTree().discovery(context, + BootStrapProperties.readApplication(environment), + microserviceName); invocation.addLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST, serversVersionedCache.data()); return loadBalancerMap .computeIfAbsent(microserviceName, name -> createLoadBalancer(microserviceName)); } private LoadBalancer createLoadBalancer(String microserviceName) { - RuleExt rule = BeanUtils.getBean(ExtensionsManager.class).createLoadBalancerRule(microserviceName); + RuleExt rule = getExtensionsManager().createLoadBalancerRule(microserviceName); return new LoadBalancer(rule, microserviceName); } diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CompatiblePathVersionMapper.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CompatiblePathVersionMapper.java deleted file mode 100644 index 062c20d8ae7..00000000000 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CompatiblePathVersionMapper.java +++ /dev/null @@ -1,63 +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.servicecomb.edge.core; - -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; -import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; -import org.apache.servicecomb.registry.version.VersionRule; -import org.apache.servicecomb.registry.version.VersionRuleUtils; - -public class CompatiblePathVersionMapper { - // v1 -> 1.0.0-2.0.0 - // v2 -> 2.0.0-3.0.0 - private final Map mapper = new ConcurrentHashMapEx<>(); - - public VersionRule getOrCreate(String pathVersion) { - return mapper.computeIfAbsent(pathVersion, this::createVersionRule); - } - - // v + number - protected VersionRule createVersionRule(String pathVersion) { - if (StringUtils.isEmpty(pathVersion) || Character.toUpperCase(pathVersion.charAt(0)) != 'V') { - throw new ServiceCombException( - String.format("pathVersion \"%s\" is invalid, format must be v+number or V+number.", pathVersion)); - } - - int number = 0; - try { - number = Integer.parseInt(pathVersion.substring(1)); - } catch (NumberFormatException e) { - throw new ServiceCombException( - String.format("pathVersion \"%s\" is invalid, format must be v+number or V+number.", pathVersion), e); - } - - if (number < 0 || number > Short.MAX_VALUE) { - throw new ServiceCombException( - String.format("pathVersion \"%s\" is invalid, version range is [0, %d].", pathVersion, Short.MAX_VALUE)); - } - - if (number == Short.MAX_VALUE) { - return VersionRuleUtils.getOrCreate(String.format("%d.0.0+", number)); - } - - return VersionRuleUtils.getOrCreate(String.format("%d.0.0-%d.0.0", number, number + 1)); - } -} diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java index 45c72609e22..56108a8cf1a 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java @@ -18,15 +18,17 @@ package org.apache.servicecomb.edge.core; import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; +import org.apache.servicecomb.common.rest.route.Utils; import org.apache.servicecomb.core.invocation.InvocationCreator; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.foundation.vertx.http.VertxServerRequestToHttpServletRequest; import org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse; -import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; -import com.netflix.config.DynamicPropertyFactory; import io.vertx.codegen.annotations.Nullable; import io.vertx.ext.web.Router; @@ -43,36 +45,36 @@ public class DefaultEdgeDispatcher extends AbstractEdgeDispatcher { private static final String KEY_PREFIX = "servicecomb.http.dispatcher.edge.default.prefix"; - private static final String KEY_WITH_VERSION = "servicecomb.http.dispatcher.edge.default.withVersion"; - private static final String KEY_PREFIX_SEGMENT_COUNT = "servicecomb.http.dispatcher.edge.default.prefixSegmentCount"; public static final String MICROSERVICE_NAME = "param0"; - public static final String VERSION = "param1"; - - private final CompatiblePathVersionMapper versionMapper = new CompatiblePathVersionMapper(); + private int prefixSegmentCount; - private boolean withVersion; + private Environment environment; - private int prefixSegmentCount; + // though this is an SPI, but add as beans. + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } @Override public int getOrder() { - return DynamicPropertyFactory.getInstance().getIntProperty(KEY_ORDER, 20_000).get(); + // can not use environment, add as beans is later than instantiate SPI + return LegacyPropertyFactory.getIntProperty(KEY_ORDER, 20_000); } @Override public boolean enabled() { - return DynamicPropertyFactory.getInstance().getBooleanProperty(KEY_ENABLED, false).get(); + return environment.getProperty(KEY_ENABLED, boolean.class, false); } @Override public void init(Router router) { - String prefix = DynamicPropertyFactory.getInstance().getStringProperty(KEY_PREFIX, "api").get(); - withVersion = DynamicPropertyFactory.getInstance().getBooleanProperty(KEY_WITH_VERSION, true).get(); - prefixSegmentCount = DynamicPropertyFactory.getInstance().getIntProperty(KEY_PREFIX_SEGMENT_COUNT, 1).get(); - String regex = generateRouteRegex(prefix, withVersion); + String prefix = environment.getProperty(KEY_PREFIX, "api"); + prefixSegmentCount = environment.getProperty(KEY_PREFIX_SEGMENT_COUNT, int.class, 1); + String regex = generateRouteRegex(prefix, false); // cookies handler are enabled by default start from 3.8.3 router.routeWithRegex(regex).handler(createBodyHandler()); @@ -87,10 +89,9 @@ String generateRouteRegex(String prefix, boolean withVersion) { protected void onRequest(RoutingContext context) { String microserviceName = extractMicroserviceName(context); - String versionRule = extractVersionRule(context); String path = Utils.findActualPath(context.request().path(), prefixSegmentCount); - requestByFilter(context, microserviceName, versionRule, path); + requestByFilter(context, microserviceName, path); } @Nullable @@ -98,20 +99,11 @@ private String extractMicroserviceName(RoutingContext context) { return context.pathParam(MICROSERVICE_NAME); } - private String extractVersionRule(RoutingContext context) { - if (withVersion) { - String pathVersion = context.pathParam(VERSION); - return versionMapper.getOrCreate(pathVersion).getVersionRule(); - } - - return DefinitionConst.VERSION_RULE_ALL; - } - - protected void requestByFilter(RoutingContext context, String microserviceName, String versionRule, String path) { + protected void requestByFilter(RoutingContext context, String microserviceName, String path) { HttpServletRequestEx requestEx = new VertxServerRequestToHttpServletRequest(context); HttpServletResponseEx responseEx = new VertxServerResponseToHttpServletResponse(context.response()); InvocationCreator creator = new EdgeInvocationCreator(context, requestEx, responseEx, - microserviceName, versionRule, path); + microserviceName, path); new RestProducerInvocationFlow(creator, requestEx, responseEx) .run(); } diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderClientFilter.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderClientFilter.java deleted file mode 100644 index 49258fab9c6..00000000000 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderClientFilter.java +++ /dev/null @@ -1,104 +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.servicecomb.edge.core; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.function.BiConsumer; - -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.common.rest.filter.HttpClientFilter; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; -import org.apache.servicecomb.swagger.invocation.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicPropertyFactory; - -public class EdgeAddHeaderClientFilter implements HttpClientFilter { - private static final Logger LOGGER = LoggerFactory.getLogger(EdgeAddHeaderClientFilter.class); - - private static final String PREFIX = "servicecomb.edge.filter.addHeader"; - - private static final String KEY_ENABLED = PREFIX + ".enabled"; - - private static final String KEY_HEADERS = PREFIX + ".allowedHeaders"; - - private List publicHeaders = new ArrayList<>(); - - private boolean enabled = false; - - public EdgeAddHeaderClientFilter() { - init(); - ConfigurationManager.getConfigInstance() - .addConfigurationListener(event -> { - if (StringUtils.startsWith(event.getPropertyName(), PREFIX)) { - LOGGER.info("Public headers config have been changed. Event=" + event.getType()); - init(); - } - }); - } - - private void init() { - enabled = DynamicPropertyFactory.getInstance().getBooleanProperty(KEY_ENABLED, false).get(); - String publicHeaderStr = DynamicPropertyFactory.getInstance().getStringProperty(KEY_HEADERS, "").get(); - String[] split = publicHeaderStr.split(","); - if (split.length > 0) { - publicHeaders = Arrays.asList(split); - } - } - - @Override - public int getOrder() { - return 0; - } - - @Override - public boolean enabled() { - return enabled; - } - - @Override - public CompletableFuture beforeSendRequestAsync(Invocation invocation, HttpServletRequestEx requestEx) { - addHeaders(invocation, requestEx::addHeader); - return CompletableFuture.completedFuture(null); - } - - public void addHeaders(Invocation invocation, BiConsumer headerAdder) { - if (!invocation.isEdge()) { - return; - } - - HttpServletRequestEx oldRequest = invocation.getRequestEx(); - publicHeaders.forEach(key -> { - String value = oldRequest.getHeader(key); - if (StringUtils.isNotEmpty(value)) { - headerAdder.accept(key, value); - } - }); - } - - @Override - public Response afterReceiveResponse(Invocation invocation, HttpServletResponseEx responseEx) { - return null; - } -} diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderFilter.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderFilter.java index 8962ed18107..92594e6157a 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderFilter.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderFilter.java @@ -16,48 +16,93 @@ */ package org.apache.servicecomb.edge.core; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.transport.rest.client.RestClientTransportContext; +import org.springframework.core.env.Environment; + +import com.google.common.eventbus.Subscribe; -public class EdgeAddHeaderFilter implements ConsumerFilter { +public class EdgeAddHeaderFilter extends AbstractFilter implements EdgeFilter { public static final String NAME = "edge-add-headers"; - private final EdgeAddHeaderClientFilter filter = new EdgeAddHeaderClientFilter(); + private static final String PREFIX = "servicecomb.edge.filter.addHeader"; + + private static final String KEY_HEADERS = PREFIX + ".allowedHeaders"; + + private final Environment environment; + + private List publicHeaders = new ArrayList<>(); + + public EdgeAddHeaderFilter(Environment environment) { + this.environment = environment; + init(); + EventManager.register(this); + } + + @Subscribe + @SuppressWarnings("unused") + public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { + for (String changed : event.getChanged()) { + if (changed.startsWith(PREFIX)) { + init(); + break; + } + } + } + + private void init() { + String publicHeaderStr = environment.getProperty(KEY_HEADERS); + if (StringUtils.isEmpty(publicHeaderStr)) { + return; + } + publicHeaders = Arrays.asList(publicHeaderStr.split(",")); + } - @Nonnull @Override public String getName() { return NAME; } @Override - public boolean isEnabledForTransport(String transport) { - return filter.enabled(); + public boolean enabledForTransport(String transport) { + return CoreConst.RESTFUL.equals(transport); } @Override - public int getOrder(InvocationType invocationType, String microservice) { - return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1991; + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1995; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - RestClientTransportContext transportContext = invocation.getTransportContext(); - if (transportContext != null) { - return CompletableFuture.completedFuture(null) - .thenAccept(v -> filter.addHeaders(invocation, transportContext.getHttpClientRequest()::putHeader)) - .thenCompose(v -> nextNode.onFilter(invocation)); + if (publicHeaders.isEmpty()) { + return nextNode.onFilter(invocation); } - // normal consumer in edge process + + RestClientTransportContext transportContext = invocation.getTransportContext(); + HttpServletRequestEx oldRequest = invocation.getRequestEx(); + publicHeaders.forEach(key -> { + String value = oldRequest.getHeader(key); + if (StringUtils.isNotEmpty(value)) { + transportContext.getHttpClientRequest().putHeader(key, value); + } + }); + return nextNode.onFilter(invocation); } } diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeBootListener.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeBootListener.java index c4a5ff4c6de..144dc2c6224 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeBootListener.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeBootListener.java @@ -17,39 +17,18 @@ package org.apache.servicecomb.edge.core; -import org.apache.commons.configuration.Configuration; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.executor.ExecutorManager; -import org.apache.servicecomb.transport.rest.client.TransportClientConfig; import org.apache.servicecomb.transport.rest.vertx.TransportConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.config.DynamicPropertyFactory; public class EdgeBootListener implements BootListener { - private static final Logger LOGGER = LoggerFactory.getLogger(EdgeBootListener.class); - @Override public void onBootEvent(BootEvent event) { if (!EventType.BEFORE_PRODUCER_PROVIDER.equals(event.getEventType())) { return; } - TransportClientConfig.setRestTransportClientCls(EdgeRestTransportClient.class); TransportConfig.setRestServerVerticle(EdgeRestServerVerticle.class); - - String defaultExecutor = DynamicPropertyFactory.getInstance() - .getStringProperty(ExecutorManager.KEY_EXECUTORS_DEFAULT, null) - .get(); - if (defaultExecutor != null) { - LOGGER.info("Edge service default executor is {}.", defaultExecutor); - return; - } - - // change default to reactive mode - Configuration configuration = (Configuration) DynamicPropertyFactory.getBackingConfigurationSource(); - configuration.setProperty(ExecutorManager.KEY_EXECUTORS_DEFAULT, ExecutorManager.EXECUTOR_REACTIVE); - LOGGER.info("Set ReactiveExecutor to be edge service default executor."); + ExecutorManager.setExecutorDefault(ExecutorManager.EXECUTOR_REACTIVE); } } diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeCoreConfiguration.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeCoreConfiguration.java index e6cb427eefd..6fa0457c7fc 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeCoreConfiguration.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeCoreConfiguration.java @@ -18,21 +18,17 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; @Configuration public class EdgeCoreConfiguration { @Bean - public EdgeBootListener edgeBootListener() { + public EdgeBootListener scbEdgeBootListener() { return new EdgeBootListener(); } @Bean - public EdgeServerCodecFilter edgeServerCodecFilter() { - return new EdgeServerCodecFilter(); - } - - @Bean - public EdgeAddHeaderFilter edgeAddHeaderFilter() { - return new EdgeAddHeaderFilter(); + public EdgeAddHeaderFilter scbEdgeAddHeaderFilter(Environment environment) { + return new EdgeAddHeaderFilter(environment); } } diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeInvocationCreator.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeInvocationCreator.java index 167077e2e4c..309548a5b38 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeInvocationCreator.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeInvocationCreator.java @@ -21,6 +21,7 @@ import org.apache.servicecomb.common.rest.RestVertxProducerInvocationCreator; import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.common.rest.locator.ServicePathManager; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.invocation.InvocationFactory; @@ -37,19 +38,19 @@ public class EdgeInvocationCreator extends RestVertxProducerInvocationCreator { protected final String microserviceName; - protected final String versionRule; - protected final String path; protected MicroserviceReferenceConfig microserviceReferenceConfig; public EdgeInvocationCreator(RoutingContext routingContext, HttpServletRequestEx requestEx, HttpServletResponseEx responseEx, - String microserviceName, String versionRule, String path) { - super(routingContext, null, null, requestEx, responseEx); + String microserviceName, String path) { + // Set endpoint before load balance because edge service will use RESTFUL transport filters. + super(routingContext, null, + SCBEngine.getInstance().getTransportManager().findTransport(CoreConst.RESTFUL).getEndpoint(), + requestEx, responseEx); this.microserviceName = microserviceName; - this.versionRule = versionRule; this.path = path; } @@ -61,10 +62,10 @@ public CompletableFuture createAsync() { protected CompletableFuture createMicroserviceReferenceConfig() { return SCBEngine.getInstance() - .createMicroserviceReferenceConfigAsync(microserviceName, versionRule) + .getOrCreateReferenceConfigAsync(microserviceName) .thenAccept(mrc -> { this.microserviceReferenceConfig = mrc; - this.microserviceMeta = mrc.getLatestMicroserviceMeta(); + this.microserviceMeta = mrc.getMicroserviceMeta(); }); } @@ -73,11 +74,6 @@ protected OperationLocator locateOperation(ServicePathManager servicePathManager return servicePathManager.consumerLocateOperation(path, requestEx.getMethod()); } - @Override - protected void initInvocationContext(Invocation invocation) { - // do not read InvocationContext from HTTP header, for security reason - } - @Override protected Invocation createInstance() { ReferenceConfig referenceConfig = microserviceReferenceConfig @@ -88,7 +84,8 @@ protected Invocation createInstance() { restOperationMeta.getOperationMeta().buildBaseConsumerRuntimeType(), null); invocation.setSync(false); - invocation.setEdge(true); + invocation.setEdge(); + invocation.setEndpoint(endpoint); // ensure transport name is correct invocation.addLocalContext(EDGE_INVOCATION_CONTEXT, Vertx.currentContext()); return invocation; diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeRestTransportClient.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeRestTransportClient.java deleted file mode 100644 index 4ccd21c0ca6..00000000000 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeRestTransportClient.java +++ /dev/null @@ -1,41 +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.servicecomb.edge.core; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.common.net.URIEndpointObject; -import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext; -import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; -import org.apache.servicecomb.transport.rest.client.Http2TransportHttpClientOptionsSPI; -import org.apache.servicecomb.transport.rest.client.HttpTransportHttpClientOptionsSPI; -import org.apache.servicecomb.transport.rest.client.RestTransportClient; - -import io.vertx.core.Context; - -public class EdgeRestTransportClient extends RestTransportClient { - @Override - protected HttpClientWithContext findHttpClientPool(Invocation invocation) { - Context invocationContext = (Context) invocation.getHandlerContext() - .get(EdgeInvocationCreator.EDGE_INVOCATION_CONTEXT); - - URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); - String clientName = endpoint.isHttp2Enabled() ? - Http2TransportHttpClientOptionsSPI.CLIENT_NAME : - HttpTransportHttpClientOptionsSPI.CLIENT_NAME; - return HttpClients.getClient(clientName, invocation.isSync(), invocationContext); - } -} diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeServerCodecFilter.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeServerCodecFilter.java deleted file mode 100644 index c6b01d6145b..00000000000 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeServerCodecFilter.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.servicecomb.edge.core; - -import java.util.concurrent.CompletableFuture; - -import javax.annotation.Nonnull; - -import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.Filter; -import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.InvocationType; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.context.TransportContext; - -public class EdgeServerCodecFilter extends RestServerCodecFilter { - public static final String NAME = "edge-server-codec"; - - @Nonnull - @Override - public String getName() { - return NAME; - } - - @Nonnull - @Override - public boolean isEnabledForInvocationType(InvocationType invocationType) { - return invocationType == InvocationType.CONSUMER; - } - - @Override - public boolean isEnabledForTransport(String transport) { - // For edge service, this filter executed before load balancer and transport is always null. - return true; - } - - @Override - public int getOrder(InvocationType invocationType, String microservice) { - return Filter.CONSUMER_LOAD_BALANCE_ORDER - 2000; - } - - @Override - public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - if (invocation.getRequestEx() == null) { - // to support normal consumer invocation in edge process - return nextNode.onFilter(invocation); - } - - return super.onFilter(invocation, nextNode); - } - - // save and restore transportContext to support edge invocation - @Override - protected CompletableFuture invokeNext(Invocation invocation, FilterNode nextNode) { - TransportContext transportContext = invocation.getTransportContext(); - return nextNode.onFilter(invocation) - .whenComplete((r, e) -> invocation.setTransportContext(transportContext)); - } -} diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedConfigurationLoader.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedConfigurationLoader.java deleted file mode 100644 index b7fd78f16b2..00000000000 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedConfigurationLoader.java +++ /dev/null @@ -1,80 +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.servicecomb.edge.core; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.regex.Pattern; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.DynamicPropertyFactory; - -public class URLMappedConfigurationLoader { - private static final Logger LOG = LoggerFactory.getLogger(URLMappedConfigurationLoader.class); - - private static final String KEY_MAPPING_PATH = ".path"; - - private static final String KEY_MAPPING_SERVICE_NAME = "%s.%s.microserviceName"; - - private static final String KEY_MAPPING_VERSION_RULE = "%s.%s.versionRule"; - - private static final String KEY_MAPPING_PREFIX_SEGMENT_COUNT = "%s.%s.prefixSegmentCount"; - - public static Map loadConfigurations( - ConcurrentCompositeConfiguration config, String configPrefix) { - Map configurations = new HashMap<>(); - Iterator configsItems = config.getKeys(configPrefix); - while (configsItems.hasNext()) { - String pathKey = configsItems.next(); - if (pathKey.endsWith(KEY_MAPPING_PATH)) { - URLMappedConfigurationItem configurationItem = new URLMappedConfigurationItem(); - String pattern = DynamicPropertyFactory.getInstance() - .getStringProperty(pathKey, null).get(); - if (StringUtils.isEmpty(pattern)) { - continue; - } - configurationItem.setPattern(Pattern.compile(pattern)); - configurationItem.setStringPattern(pattern); - String pathKeyItem = pathKey - .substring(configPrefix.length() + 1, pathKey.length() - KEY_MAPPING_PATH.length()); - configurationItem.setMicroserviceName(DynamicPropertyFactory.getInstance() - .getStringProperty(String.format(KEY_MAPPING_SERVICE_NAME, configPrefix, pathKeyItem), null).get()); - if (StringUtils.isEmpty(configurationItem.getMicroserviceName())) { - continue; - } - configurationItem.setPrefixSegmentCount(DynamicPropertyFactory.getInstance() - .getIntProperty(String.format(KEY_MAPPING_PREFIX_SEGMENT_COUNT, configPrefix, pathKeyItem), 0).get()); - configurationItem.setVersionRule(DynamicPropertyFactory.getInstance() - .getStringProperty(String.format(KEY_MAPPING_VERSION_RULE, configPrefix, pathKeyItem), "0.0.0+").get()); - configurations.put(pathKeyItem, configurationItem); - } - } - logConfigurations(configurations); - return configurations; - } - - private static void logConfigurations(Map configurations) { - configurations.forEach((key, item) -> LOG.info("config item: key=" + key + ";pattern=" + item.getStringPattern() - + ";service=" + item.getMicroserviceName() + ";versionRule=" + item.getVersionRule())); - } -} diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java index e291c1ba3a9..d4e456c098f 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java @@ -21,18 +21,23 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; +import org.apache.servicecomb.common.rest.route.URLMappedConfigurationItem; +import org.apache.servicecomb.common.rest.route.URLMappedConfigurationLoader; +import org.apache.servicecomb.common.rest.route.Utils; +import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.core.invocation.InvocationCreator; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.foundation.vertx.http.VertxServerRequestToHttpServletRequest; import org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse; import org.apache.servicecomb.transport.rest.vertx.RestBodyHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.DynamicPropertyFactory; +import com.google.common.eventbus.Subscribe; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; @@ -42,8 +47,6 @@ * Provide a URL mapping based dispatcher. Users configure witch URL patterns dispatch to a target service. */ public class URLMappedEdgeDispatcher extends AbstractEdgeDispatcher { - private static final Logger LOG = LoggerFactory.getLogger(URLMappedEdgeDispatcher.class); - public static final String CONFIGURATION_ITEM = "URLMappedConfigurationItem"; private static final String PATTERN_ANY = "/(.*)"; @@ -58,7 +61,16 @@ public class URLMappedEdgeDispatcher extends AbstractEdgeDispatcher { private Map configurations = new HashMap<>(); + private Environment environment; + public URLMappedEdgeDispatcher() { + EventManager.register(this); + } + + // though this is an SPI, but add as beans. + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; if (this.enabled()) { loadConfigurations(); } @@ -71,18 +83,18 @@ public Map getConfigurations() { @Override public int getOrder() { - return DynamicPropertyFactory.getInstance().getIntProperty(KEY_ORDER, 30_000).get(); + return LegacyPropertyFactory.getIntProperty(KEY_ORDER, 30_000); } @Override public boolean enabled() { - return DynamicPropertyFactory.getInstance().getBooleanProperty(KEY_ENABLED, false).get(); + return environment.getProperty(KEY_ENABLED, boolean.class, false); } @Override public void init(Router router) { // cookies handler are enabled by default start from 3.8.3 - String pattern = DynamicPropertyFactory.getInstance().getStringProperty(KEY_PATTERN, PATTERN_ANY).get(); + String pattern = environment.getProperty(KEY_PATTERN, PATTERN_ANY); router.routeWithRegex(pattern).failureHandler(this::onFailure) .handler((PlatformHandler) URLMappedEdgeDispatcher.this::preCheck) .handler(createBodyHandler()) @@ -90,15 +102,17 @@ public void init(Router router) { } private void loadConfigurations() { - ConcurrentCompositeConfiguration config = (ConcurrentCompositeConfiguration) DynamicPropertyFactory - .getBackingConfigurationSource(); - configurations = URLMappedConfigurationLoader.loadConfigurations(config, KEY_MAPPING_PREFIX); - config.addConfigurationListener(event -> { - if (event.getPropertyName().startsWith(KEY_MAPPING_PREFIX)) { - LOG.info("Map rule have been changed. Reload configurations. Event=" + event.getType()); - configurations = URLMappedConfigurationLoader.loadConfigurations(config, KEY_MAPPING_PREFIX); + configurations = URLMappedConfigurationLoader.loadConfigurations(environment, KEY_MAPPING_PREFIX); + } + + @Subscribe + public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { + for (String changed : event.getChanged()) { + if (changed.startsWith(KEY_MAPPING_PREFIX)) { + loadConfigurations(); + break; } - }); + } } protected void preCheck(RoutingContext context) { @@ -133,7 +147,7 @@ protected void requestByFilter(RoutingContext context, URLMappedConfigurationIte HttpServletRequestEx requestEx = new VertxServerRequestToHttpServletRequest(context); HttpServletResponseEx responseEx = new VertxServerResponseToHttpServletResponse(context.response()); InvocationCreator creator = new EdgeInvocationCreator(context, requestEx, responseEx, - configurationItem.getMicroserviceName(), configurationItem.getVersionRule(), path); + configurationItem.getMicroserviceName(), path); new RestProducerInvocationFlow(creator, requestEx, responseEx) .run(); } diff --git a/edge/edge-core/src/main/resources/META-INF/microservice.yaml b/edge/edge-core/src/main/resources/META-INF/microservice.yaml new file mode 100644 index 00000000000..a456c2868b2 --- /dev/null +++ b/edge/edge-core/src/main/resources/META-INF/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb-config-order: -500 + +servicecomb: + context: + # do not decode invocation context for edge service by default + decodeInvocationContext: false diff --git a/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpClientFilter b/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpClientFilter deleted file mode 100644 index c07a4b7621f..00000000000 --- a/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpClientFilter +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.edge.core.EdgeAddHeaderClientFilter diff --git a/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher b/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher index 0f1d5d6dea7..6a672cefffd 100644 --- a/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher +++ b/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher @@ -17,4 +17,4 @@ org.apache.servicecomb.edge.core.DefaultEdgeDispatcher org.apache.servicecomb.edge.core.URLMappedEdgeDispatcher -org.apache.servicecomb.edge.core.CommonHttpEdgeDispatcher \ No newline at end of file +org.apache.servicecomb.edge.core.CommonHttpEdgeDispatcher diff --git a/edge/edge-core/src/main/resources/META-INF/spring.factories b/edge/edge-core/src/main/resources/META-INF/spring.factories deleted file mode 100644 index c927186a4ea..00000000000 --- a/edge/edge-core/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,18 +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. -# -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.edge.core.EdgeCoreConfiguration diff --git a/edge/edge-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/edge/edge-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..aa087dba015 --- /dev/null +++ b/edge/edge-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.edge.core.EdgeCoreConfiguration diff --git a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestCompatiblePathVersionMapper.java b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestCompatiblePathVersionMapper.java deleted file mode 100644 index 390da6ad615..00000000000 --- a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestCompatiblePathVersionMapper.java +++ /dev/null @@ -1,76 +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.servicecomb.edge.core; - -import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; -import org.apache.servicecomb.registry.version.VersionRule; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestCompatiblePathVersionMapper { - CompatiblePathVersionMapper mapper = new CompatiblePathVersionMapper(); - - @Test - public void getOrCreate() { - VersionRule versionRule = mapper.getOrCreate("v1"); - - Assertions.assertEquals("1.0.0.0-2.0.0.0", versionRule.getVersionRule()); - } - - @Test - public void createVersionRule_empty() { - ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, - () -> mapper.getOrCreate("")); - Assertions.assertEquals("pathVersion \"\" is invalid, format must be v+number or V+number.", exception.getMessage()); - } - - @Test - public void createVersionRule_invalidFormat() { - ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, - () -> mapper.getOrCreate("a1")); - Assertions.assertEquals("pathVersion \"a1\" is invalid, format must be v+number or V+number.", exception.getMessage()); - } - - @Test - public void createVersionRule_invalidNumber() { - ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, - () -> mapper.getOrCreate("va")); - Assertions.assertEquals("pathVersion \"va\" is invalid, format must be v+number or V+number.", exception.getMessage()); - } - - @Test - public void createVersionRule_tooSmall() { - ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, - () -> mapper.getOrCreate("v-1")); - Assertions.assertEquals("pathVersion \"v-1\" is invalid, version range is [0, 32767].", exception.getMessage()); - } - - @Test - public void createVersionRule_tooBig() { - ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, - () -> mapper.getOrCreate("v32768")); - Assertions.assertEquals("pathVersion \"v32768\" is invalid, version range is [0, 32767].", exception.getMessage()); - } - - @Test - public void createVersionRule_32767() { - VersionRule versionRule = mapper.getOrCreate("v32767"); - - Assertions.assertEquals("32767.0.0.0+", versionRule.getVersionRule()); - } -} diff --git a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestEdgeBootListener.java b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestEdgeBootListener.java deleted file mode 100644 index b9341c600c5..00000000000 --- a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestEdgeBootListener.java +++ /dev/null @@ -1,77 +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.servicecomb.edge.core; - -import org.apache.servicecomb.core.BootListener.BootEvent; -import org.apache.servicecomb.core.BootListener.EventType; -import org.apache.servicecomb.core.executor.ExecutorManager; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; - -import com.netflix.config.DynamicPropertyFactory; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class TestEdgeBootListener { - EdgeBootListener listener = new EdgeBootListener(); - - BootEvent bootEvent = new BootEvent(); - - @BeforeEach - public void setup() { - ArchaiusUtils.resetConfig(); - } - - @AfterEach - public void teardown() { - ArchaiusUtils.resetConfig(); - System.clearProperty(ExecutorManager.KEY_EXECUTORS_DEFAULT); - } - - @Test - public void onBootEvent_ignore() { - System.setProperty(ExecutorManager.KEY_EXECUTORS_DEFAULT, ExecutorManager.EXECUTOR_DEFAULT); - - bootEvent.setEventType(EventType.AFTER_CONSUMER_PROVIDER); - listener.onBootEvent(bootEvent); - - Assertions.assertEquals(ExecutorManager.EXECUTOR_DEFAULT, - DynamicPropertyFactory.getInstance().getStringProperty(ExecutorManager.KEY_EXECUTORS_DEFAULT, null).get()); - } - - @Test - public void onBootEvent_accept_notChange() { - System.setProperty(ExecutorManager.KEY_EXECUTORS_DEFAULT, ExecutorManager.EXECUTOR_DEFAULT); - - bootEvent.setEventType(EventType.BEFORE_PRODUCER_PROVIDER); - listener.onBootEvent(bootEvent); - - Assertions.assertEquals(ExecutorManager.EXECUTOR_DEFAULT, - DynamicPropertyFactory.getInstance().getStringProperty(ExecutorManager.KEY_EXECUTORS_DEFAULT, null).get()); - } - - @Test - public void onBootEvent_change() { - bootEvent.setEventType(EventType.BEFORE_PRODUCER_PROVIDER); - listener.onBootEvent(bootEvent); - - Assertions.assertEquals(ExecutorManager.EXECUTOR_REACTIVE, - DynamicPropertyFactory.getInstance().getStringProperty(ExecutorManager.KEY_EXECUTORS_DEFAULT, null).get()); - } -} diff --git a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java index db0064c2a06..046adfaa3a2 100644 --- a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java +++ b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java @@ -17,33 +17,46 @@ package org.apache.servicecomb.edge.core; +import java.util.HashMap; import java.util.Map; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.common.rest.route.URLMappedConfigurationItem; +import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.transport.rest.vertx.RestBodyHandler; 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 org.mockito.Mockito; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.MutablePropertySources; import io.vertx.ext.web.RoutingContext; public class TestURLMappedEdgeDispatcher { + ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); + @BeforeEach public void setUp() throws Exception { } @AfterEach public void tearDown() { - ArchaiusUtils.resetConfig(); } @Test public void testConfigurations() { - ArchaiusUtils.setProperty("servicecomb.http.dispatcher.edge.url.enabled", true); - + EnumerablePropertySource propertySource = Mockito.mock(EnumerablePropertySource.class); + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + mutablePropertySources.addLast(propertySource); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); + Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { + }); + Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.enabled", boolean.class, false)) + .thenReturn(true); URLMappedEdgeDispatcher dispatcher = new URLMappedEdgeDispatcher(); + dispatcher.setEnvironment(environment); Map items = dispatcher.getConfigurations(); Assertions.assertEquals(items.size(), 0); @@ -51,10 +64,26 @@ public void testConfigurations() { Mockito.when(context.get(RestBodyHandler.BYPASS_BODY_HANDLER)).thenReturn(Boolean.TRUE); dispatcher.onRequest(context); - ArchaiusUtils.setProperty("servicecomb.http.dispatcher.edge.url.mappings.service1.path", "/a/b/c/.*"); - ArchaiusUtils.setProperty("servicecomb.http.dispatcher.edge.url.mappings.service1.microserviceName", "serviceName"); - ArchaiusUtils.setProperty("servicecomb.http.dispatcher.edge.url.mappings.service1.prefixSegmentCount", 2); - ArchaiusUtils.setProperty("servicecomb.http.dispatcher.edge.url.mappings.service1.versionRule", "2.0.0+"); + Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { + "servicecomb.http.dispatcher.edge.url.mappings.service1.path", + "servicecomb.http.dispatcher.edge.url.mappings.service1.microserviceName", + "servicecomb.http.dispatcher.edge.url.mappings.service1.prefixSegmentCount", + "servicecomb.http.dispatcher.edge.url.mappings.service1.versionRule" + }); + Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.mappings.service1.path")) + .thenReturn("/a/b/c/.*"); + Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.mappings.service1.microserviceName")) + .thenReturn("serviceName"); + Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.mappings.service1.prefixSegmentCount", + int.class, 0)) + .thenReturn(2); + Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.mappings.service1.versionRule", + "0.0.0+")) + .thenReturn("2.0.0+"); + Map latest = new HashMap<>(); + latest.put("servicecomb.http.dispatcher.edge.url.mappings.service1.path", "/a/b/c/.*"); + dispatcher.onConfigurationChangedEvent(ConfigurationChangedEvent.createIncremental(latest, new HashMap<>())); + items = dispatcher.getConfigurations(); Assertions.assertEquals(items.size(), 1); URLMappedConfigurationItem item = items.get("service1"); @@ -63,8 +92,23 @@ public void testConfigurations() { Assertions.assertEquals(item.getStringPattern(), "/a/b/c/.*"); Assertions.assertEquals(item.getVersionRule(), "2.0.0+"); - ArchaiusUtils.setProperty("servicecomb.http.dispatcher.edge.url.mappings.service2.versionRule", "2.0.0+"); - ArchaiusUtils.setProperty("servicecomb.http.dispatcher.edge.url.mappings.service3.path", "/b/c/d/.*"); + Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { + "servicecomb.http.dispatcher.edge.url.mappings.service1.path", + "servicecomb.http.dispatcher.edge.url.mappings.service1.microserviceName", + "servicecomb.http.dispatcher.edge.url.mappings.service1.prefixSegmentCount", + "servicecomb.http.dispatcher.edge.url.mappings.service1.versionRule", + "servicecomb.http.dispatcher.edge.url.mappings.service2.versionRule", + "servicecomb.http.dispatcher.edge.url.mappings.service3.path" + }); + Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.mappings.service2.versionRule")) + .thenReturn("2.0.0+"); + Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.mappings.service3.path")) + .thenReturn("/b/c/d/.*"); + + latest = new HashMap<>(); + latest.put("servicecomb.http.dispatcher.edge.url.mappings.service3.path", "/a/b/c/.*"); + dispatcher.onConfigurationChangedEvent(ConfigurationChangedEvent.createIncremental(latest, new HashMap<>())); + items = dispatcher.getConfigurations(); Assertions.assertEquals(items.size(), 1); item = items.get("service1"); diff --git a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestUtils.java b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestUtils.java index f7fcfa18c8e..38e8fa837d5 100644 --- a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestUtils.java +++ b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestUtils.java @@ -17,6 +17,7 @@ package org.apache.servicecomb.edge.core; +import org.apache.servicecomb.common.rest.route.Utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/edge/pom.xml b/edge/pom.xml index 1ef9644f852..e11f4e088b6 100644 --- a/edge/pom.xml +++ b/edge/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default edge diff --git a/foundations/foundation-common/pom.xml b/foundations/foundation-common/pom.xml index d949af1af13..2f5a18878b4 100644 --- a/foundations/foundation-common/pom.xml +++ b/foundations/foundation-common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT foundations foundation-common @@ -54,6 +54,12 @@ org.apache.httpcomponents httpclient + + + commons-logging + commons-logging + + jakarta.ws.rs @@ -99,7 +105,7 @@ io.vertx vertx-core - + org.apache.logging.log4j log4j-slf4j-impl @@ -115,9 +121,5 @@ jmockit test - - com.netflix.archaius - archaius-core - diff --git a/foundations/foundation-common/src/main/java/com/fasterxml/jackson/databind/ObjectMapperUtils.java b/foundations/foundation-common/src/main/java/com/fasterxml/jackson/databind/ObjectMapperUtils.java deleted file mode 100644 index 5be6d73c710..00000000000 --- a/foundations/foundation-common/src/main/java/com/fasterxml/jackson/databind/ObjectMapperUtils.java +++ /dev/null @@ -1,28 +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 com.fasterxml.jackson.databind; - -import com.fasterxml.jackson.core.JsonParser; - -public final class ObjectMapperUtils { - private ObjectMapperUtils() { - } - - public static DeserializationContext createDeserializationContext(ObjectMapper mapper, JsonParser jp) { - return mapper.createDeserializationContext(jp, mapper.getDeserializationConfig()); - } -} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/FoundationCommonAuthConfiguration.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/FoundationCommonAuthConfiguration.java index 19ea1e752e3..7faea74a3b5 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/FoundationCommonAuthConfiguration.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/FoundationCommonAuthConfiguration.java @@ -20,7 +20,7 @@ @Configuration public class FoundationCommonAuthConfiguration { - public ShaAKSKCipher shaAKSKCipher() { + public ShaAKSKCipher scbShaAKSKCipher() { return new ShaAKSKCipher(); } } diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/Holder.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/Holder.java index ebc92a34ebd..9e9752c6a30 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/Holder.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/Holder.java @@ -16,7 +16,7 @@ */ package org.apache.servicecomb.foundation.common; -// do not use javax.xml.ws.Holder, use this one. Because JDK 11 above do not have javax.ws.Holder +// do not use javax.xml.ws.Holder, use this one. Because JDK 11 above do not have jakarta.ws.Holder public final class Holder { /** diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/LegacyPropertyFactory.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/LegacyPropertyFactory.java new file mode 100644 index 00000000000..ada58bdf3e7 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/LegacyPropertyFactory.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.common; + +import org.springframework.core.env.Environment; + +import com.google.common.annotations.VisibleForTesting; + +/** + * Provider a convenient way to get property value in static context. + * + * NOTE: This way is not commented and only for legacy code without too much refactoring. + * And make sure to use this class when spring bean context is already initialized. + */ +public class LegacyPropertyFactory { + private static Environment environment; + + public LegacyPropertyFactory(Environment environment) { + LegacyPropertyFactory.environment = environment; + } + + @VisibleForTesting + public static void setEnvironment(Environment environment) { + LegacyPropertyFactory.environment = environment; + } + + public static T getProperty(String key, Class targetType) { + return environment.getProperty(key, targetType); + } + + public static T getProperty(String key, Class targetType, T defaultValue) { + return environment.getProperty(key, targetType, defaultValue); + } + + public static boolean getBooleanProperty(String key, boolean defaultValue) { + return environment.getProperty(key, boolean.class, defaultValue); + } + + public static int getIntProperty(String key, int defaultValue) { + return environment.getProperty(key, int.class, defaultValue); + } + + public static long getLongProperty(String key, long defaultValue) { + return environment.getProperty(key, long.class, defaultValue); + } + + public static String getStringProperty(String key) { + return environment.getProperty(key); + } + + public static String getStringProperty(String key, String defaultValue) { + return environment.getProperty(key, defaultValue); + } + + public static Environment getEnvironment() { + return environment; + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/NamedThreadFactory.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/NamedThreadFactory.java index c6be9c1e0db..3b79c9c8e1a 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/NamedThreadFactory.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/NamedThreadFactory.java @@ -43,7 +43,9 @@ public NamedThreadFactory(String prefix) { */ @Override public Thread newThread(Runnable r) { - return new Thread(r, prefix + "-" + threadNumber.getAndIncrement()); + Thread thread = new Thread(r, prefix + "-" + threadNumber.getAndIncrement()); + thread.setDaemon(true); + return thread; } public String getPrefix() { diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/ParameterizedTypeUtil.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/ParameterizedTypeUtil.java new file mode 100644 index 00000000000..025c08acbf3 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/ParameterizedTypeUtil.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.servicecomb.foundation.common; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Objects; +import java.util.StringJoiner; + +/** + * Utility class for creating ParameterizedType. + */ +public class ParameterizedTypeUtil implements ParameterizedType { + private final Type[] actualTypeArguments; + + private final Class rawType; + + private ParameterizedTypeUtil(Class rawType, Type[] actualTypeArguments) { + this.actualTypeArguments = actualTypeArguments; + this.rawType = rawType; + } + + public static ParameterizedType make(Class rawType, Type... actualTypeArguments) { + return new ParameterizedTypeUtil(rawType, actualTypeArguments); + } + + @Override + public Type[] getActualTypeArguments() { + return this.actualTypeArguments; + } + + @Override + public Type getRawType() { + return rawType; + } + + @Override + public Type getOwnerType() { + return null; + } + + @Override + public boolean equals(Object o) { + if (o instanceof ParameterizedType that) { + + if (this == that) { + return true; + } + + Type thatRawType = that.getRawType(); + + return Objects.equals(rawType, thatRawType) && + Arrays.equals(actualTypeArguments, that.getActualTypeArguments()); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Arrays.hashCode(actualTypeArguments) ^ + Objects.hashCode(rawType); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append(rawType.getName()); + + if (actualTypeArguments != null) { + StringJoiner sj = new StringJoiner(", ", "<", ">"); + sj.setEmptyValue(""); + for (Type t : actualTypeArguments) { + sj.add(t.getTypeName()); + } + sb.append(sj); + } + + return sb.toString(); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/EnumUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/EnumUtils.java index e845bc3ea45..b25bcfa1504 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/EnumUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/EnumUtils.java @@ -18,6 +18,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.lang.reflect.Type; import java.util.Arrays; import java.util.stream.Stream; @@ -40,7 +41,7 @@ static Stream findEnumFields(Class cls) { .filter(field -> isEnumField(cls, field)); } - static boolean isDynamicEnum(Class cls) { - return DynamicEnum.class.isAssignableFrom(cls); + static boolean isDynamicEnum(Type cls) { + return cls instanceof DynamicEnum; } } diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/ServiceCombConstants.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/ServiceCombConstants.java index 8cf6db4cd42..aedae7c1a86 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/ServiceCombConstants.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/ServiceCombConstants.java @@ -30,7 +30,7 @@ public interface ServiceCombConstants { String DEFAULT_TRACING_COLLECTOR_ADDRESS = "http://127.0.0.1:9411"; - String CONFIG_KEY_SPLITER = "_"; + String CONFIG_KEY_SPLITTER = "_"; String CONFIG_FRAMEWORK_DEFAULT_NAME = "servicecomb-java-chassis"; diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/cache/VersionedCache.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/cache/VersionedCache.java index 6580174861e..b30e13862b4 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/cache/VersionedCache.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/cache/VersionedCache.java @@ -30,7 +30,7 @@ interface IsEmpty { boolean isEmpty(); } - protected int cacheVersion; + protected volatile int cacheVersion; // an optional name protected String name; diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/PaaSResourceUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/PaaSResourceUtils.java index 77270ec2d07..fe11a3fe763 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/PaaSResourceUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/PaaSResourceUtils.java @@ -118,7 +118,7 @@ public static List getSortedResources(String locationPattern, String s return resList; } - public static List getSortedPorperties(String locationPattern) { + public static List getSortedProperties(String locationPattern) { return getSortedResources(locationPattern, PROPERTIES_SUFFIX); } diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PropertiesLoader.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PropertiesLoader.java index 4f8827a6748..ae4500e8aef 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PropertiesLoader.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PropertiesLoader.java @@ -40,7 +40,7 @@ public List getFoundResList() { public T load() throws Exception { Properties props = new Properties(); for (String locationPattern : locationPatternList) { - List resList = PaaSResourceUtils.getSortedPorperties(locationPattern); + List resList = PaaSResourceUtils.getSortedProperties(locationPattern); foundResList.addAll(resList); PaaSPropertiesLoaderUtils.fillAllProperties(props, resList); } diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java index aad5e69fbdf..50b474050d9 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java @@ -56,7 +56,7 @@ public static void unregister(Object listener) { } @Bean - public EventBus eventBus() { + public EventBus scbEventBus() { return eventBus; } } diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatus.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatus.java index 15489ba95a9..45806dcc53f 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatus.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatus.java @@ -16,9 +16,9 @@ */ package org.apache.servicecomb.foundation.common.http; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.Response.Status.Family; -import javax.ws.rs.core.Response.StatusType; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status.Family; +import jakarta.ws.rs.core.Response.StatusType; public class HttpStatus implements StatusType { public static boolean isSuccess(int code) { diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatusManager.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatusManager.java index 44e91453818..8a995f74389 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatusManager.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatusManager.java @@ -19,8 +19,8 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.Response.StatusType; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatusUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatusUtils.java index 4dac0e4259b..3c4693c9eab 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatusUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatusUtils.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.foundation.common.http; -import javax.ws.rs.core.Response.StatusType; +import jakarta.ws.rs.core.Response.StatusType; public final class HttpStatusUtils { private static final HttpStatusManager MGR = new HttpStatusManager(); diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpUtils.java index 4793be59b97..626cb136030 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpUtils.java @@ -43,9 +43,8 @@ public static String parseParamFromHeaderValue(String headerValue, String paramN if (idx == -1) { continue; } - - if (paramName.equalsIgnoreCase(value.substring(0, idx))) { - return value.substring(idx + 1); + if (paramName.equalsIgnoreCase(value.substring(0, idx).trim())) { + return value.substring(idx + 1).replaceAll("\"", "").trim(); } } return null; diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/IpPort.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/IpPort.java index d8515eea13a..66aae3ebca7 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/IpPort.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/IpPort.java @@ -22,9 +22,9 @@ import com.google.common.base.Objects; public class IpPort { - private String hostOrIp; + private volatile String hostOrIp; - private int port; + private volatile int port; private volatile InetSocketAddress socketAddress; diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java index 34d2d413549..fd8cc1f7a29 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java @@ -262,7 +262,7 @@ public static IpPort parseIpPort(String scheme, String authority) { /** * 对于配置为0.0.0.0的地址,let it go * schema, e.g. http - * adddress, e.g 0.0.0.0:8080 + * address, e.g 0.0.0.0:8080 * return 实际监听的地址 */ public static String getRealListenAddress(String schema, String address) { diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/URIEndpointObject.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/URIEndpointObject.java index 03b0ffc9647..78b633c1183 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/URIEndpointObject.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/URIEndpointObject.java @@ -35,24 +35,32 @@ public class URIEndpointObject extends IpPort { private static final String PROTOCOL_KEY = "protocol"; + private static final String WEBSOCKET_ENABLED_KEY = "websocketEnabled"; + private static final String HTTP2 = "http2"; private final boolean sslEnabled; private boolean http2Enabled; - private final Map> querys; + private boolean websocketEnabled; + + private final Map> queries; + + private final String schema; public URIEndpointObject(String endpoint) { URI uri = URI.create(endpoint); + schema = uri.getScheme(); setHostOrIp(uri.getHost()); if (uri.getPort() < 0) { // do not use default port throw new IllegalArgumentException("port not specified."); } setPort(uri.getPort()); - querys = splitQuery(uri); + queries = splitQuery(uri); sslEnabled = Boolean.parseBoolean(getFirst(SSL_ENABLED_KEY)); + websocketEnabled = Boolean.parseBoolean(getFirst(WEBSOCKET_ENABLED_KEY)); String httpVersion = getFirst(PROTOCOL_KEY); if (HTTP2.equals(httpVersion)) { http2Enabled = true; @@ -73,16 +81,24 @@ public boolean isSslEnabled() { return sslEnabled; } + public boolean isWebsocketEnabled() { + return websocketEnabled; + } + public boolean isHttp2Enabled() { return http2Enabled; } + public String getSchema() { + return this.schema; + } + public List getQuery(String key) { - return querys.get(key); + return queries.get(key); } public String getFirst(String key) { - List values = querys.get(key); + List values = queries.get(key); // it's impossible that values is not null and size is 0 if (values == null) { return null; diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/AbstractPart.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/AbstractPart.java index b79699d4763..0c849a5d83a 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/AbstractPart.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/AbstractPart.java @@ -22,8 +22,9 @@ import java.util.Collection; import javax.activation.MimetypesFileTypeMap; -import javax.servlet.http.Part; -import javax.ws.rs.core.MediaType; + +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.MediaType; public class AbstractPart implements Part { private static final MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap(); diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePart.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePart.java index d44c40b92d5..1bd9ddfd952 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePart.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePart.java @@ -18,9 +18,9 @@ package org.apache.servicecomb.foundation.common.part; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; import org.apache.commons.io.FileUtils; @@ -41,7 +41,7 @@ public FilePart(String name, File file) { @Override public InputStream getInputStream() throws IOException { - return new FileInputStream(file); + return Files.newInputStream(file.toPath()); } @Override diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePartForSend.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePartForSend.java index f86e696a66e..b2252361997 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePartForSend.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePartForSend.java @@ -16,7 +16,7 @@ */ package org.apache.servicecomb.foundation.common.part; -import javax.servlet.http.Part; +import jakarta.servlet.http.Part; public interface FilePartForSend extends Part { boolean isDeleteAfterFinished(); diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.java index dfbd3e27244..bc6a648b361 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.java @@ -27,10 +27,6 @@ public final class BeanUtils { - public static final String DEFAULT_BEAN_CORE_RESOURCE = "classpath*:META-INF/spring/scb-core-bean.xml"; - - public static final String DEFAULT_BEAN_NORMAL_RESOURCE = "classpath*:META-INF/spring/*.bean.xml"; - private static ApplicationContext context; private BeanUtils() { diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ConditionWaiter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ConditionWaiter.java new file mode 100644 index 00000000000..4641ca3d233 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ConditionWaiter.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.servicecomb.foundation.common.utils; + +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +public class ConditionWaiter { + private final AtomicReference dataReference; + + private final AtomicBoolean isComplete; + + private final long sleepDuration; + + private final TimeUnit timeUnit; + + private final ExecutorService executorService; + + public ConditionWaiter(T initialData, long sleepDuration, TimeUnit timeUnit) { + this.dataReference = new AtomicReference<>(initialData); + this.isComplete = new AtomicBoolean(false); + this.sleepDuration = sleepDuration; + this.timeUnit = timeUnit; + this.executorService = Executors.newSingleThreadExecutor(); + } + + public T waitForCompletion() { + while (!isComplete.get()) { + SleepUtil.sleep(sleepDuration, timeUnit); + } + return dataReference.get(); + } + + public void setData(T newData) { + dataReference.set(newData); + } + + public void executeTaskAsync(Callable task) { + CompletableFuture.supplyAsync(() -> { + try { + return task.call(); + } catch (Exception e) { + throw new RuntimeException("Task execution failed", e); + } + }, executorService).thenAccept(result -> { + setData(result); + isComplete.set(true); + }); + } + + public static class SleepUtil { + public static void sleep(long duration, TimeUnit timeUnit) { + try { + timeUnit.sleep(duration); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + System.out.println("Thread was interrupted during sleep!"); + } + } + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/GenericsUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/GenericsUtils.java index 0429b82da70..d872236082e 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/GenericsUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/GenericsUtils.java @@ -24,14 +24,14 @@ public final class GenericsUtils { /** * check if XXX.class is generic type. see TestGenericsUtils for details meaning. * This method is provided for API compatibility for RestTemplate. Following code: - * - * + *

+ *{@code * List>> response = consumers.getSCBRestTemplate() * postForObject("/testListObjectParam", request, List.class); - * + * } * * should work for versions of 1.*. This is because java-chassis can read type info from swaggers. - * + *

* Start from 2.x, the best practise to write this code is to use ParameterizedTypeReference provided by RestTemplate * exchange method. */ diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairUtils.java index 361e62fa032..91fed2ce1c8 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairUtils.java @@ -31,23 +31,22 @@ import java.security.spec.X509EncodedKeySpec; import java.util.Base64; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.netflix.config.DynamicPropertyFactory; - public class KeyPairUtils { private static final Logger LOGGER = LoggerFactory.getLogger(KeyPairUtils.class); - private static final String KEY_GENERATOR_ALGORITHM = DynamicPropertyFactory.getInstance() - .getStringProperty("servicecomb.publicKey.accessControl.keyGeneratorAlgorithm", "RSA").get();; + private static final String KEY_GENERATOR_ALGORITHM = LegacyPropertyFactory.getStringProperty( + "servicecomb.publicKey.accessControl.keyGeneratorAlgorithm", "RSA"); - private static final String SIGN_ALG = DynamicPropertyFactory.getInstance() - .getStringProperty("servicecomb.publicKey.accessControl.signAlgorithm", "SHA256withRSA").get(); + private static final String SIGN_ALG = LegacyPropertyFactory.getStringProperty( + "servicecomb.publicKey.accessControl.signAlgorithm", "SHA256withRSA"); - private static final int KEY_SIZE = DynamicPropertyFactory.getInstance() - .getIntProperty("servicecomb.publicKey.accessControl.keySize", 2048).get(); + private static final int KEY_SIZE = LegacyPropertyFactory.getIntProperty( + "servicecomb.publicKey.accessControl.keySize", 2048); private static final Base64.Encoder encoder = Base64.getEncoder(); @@ -73,7 +72,7 @@ public static KeyPairEntry generateALGKeyPair() { PrivateKey privKey = keyPair.getPrivate(); return new KeyPairEntry(privKey, pubKey, encoder.encodeToString(pubKey.getEncoded())); } catch (NoSuchAlgorithmException e) { - LOGGER.error("generate rsa keypair faild"); + LOGGER.error("generate rsa keypair failed"); throw new IllegalStateException("perhaps error occurred on jre"); } } diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/LambdaUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/LambdaUtils.java new file mode 100644 index 00000000000..09265d57e2e --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/LambdaUtils.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.common.utils; + +import java.util.function.Consumer; + +public class LambdaUtils { + + public static Consumer ignoreException(Consumer consumer) { + return item -> { + try { + consumer.accept(item); + } catch (Throwable e) { + } + }; + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/MuteExceptionUtil.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/MuteExceptionUtil.java new file mode 100644 index 00000000000..334ae13808f --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/MuteExceptionUtil.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.servicecomb.foundation.common.utils; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MuteExceptionUtil { + + public interface FunctionWithException { + R apply(T t) throws Exception; + } + + public interface FunctionWithDoubleParam { + R apply(T1 t1, T2 t2) throws Exception; + } + + private static final Logger LOGGER = LoggerFactory.getLogger(MuteExceptionUtil.class); + + public static class MuteExceptionUtilBuilder { + + private String logMessage; + + private Object[] customMessageParams; + + public MuteExceptionUtilBuilder withLog(String message, Object... params) { + this.logMessage = message; + this.customMessageParams = params; + return this; + } + + private String getLogMessage(String defaultMessage) { + return logMessage != null ? logMessage : defaultMessage; + } + + public R executeFunction(FunctionWithException function, T t) { + try { + return function.apply(t); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute Function failure..."), customMessageParams, e); + return null; + } + } + + public T executeSupplier(Supplier supplier) { + try { + return supplier.get(); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute Supplier failure..."), customMessageParams, e); + return null; + } + } + + public T executeCompletableFuture(CompletableFuture completableFuture) { + try { + return completableFuture.get(); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute CompletableFuture failure..."), customMessageParams, e); + return null; + } + } + + public R executeFunctionWithDoubleParam(FunctionWithDoubleParam function, T1 t1, T2 t2) { + try { + return function.apply(t1, t2); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute FunctionWithDoubleParam failure..."), customMessageParams, e); + return null; + } + } + } + + public static MuteExceptionUtilBuilder builder() { + return new MuteExceptionUtilBuilder(); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/PartUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/PartUtils.java index cb9b23393be..61bbf442e11 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/PartUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/PartUtils.java @@ -19,14 +19,14 @@ import java.io.File; import java.io.InputStream; -import javax.servlet.http.Part; - import org.apache.servicecomb.foundation.common.part.FilePart; import org.apache.servicecomb.foundation.common.part.InputStreamPart; import org.apache.servicecomb.foundation.common.part.ResourcePart; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.Resource; +import jakarta.servlet.http.Part; + public final class PartUtils { private PartUtils() { } diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/JavaxServletPartDeserializer.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/JavaxServletPartDeserializer.java index 98b489f9c12..5b4f80b9ddb 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/JavaxServletPartDeserializer.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/JavaxServletPartDeserializer.java @@ -19,12 +19,12 @@ import java.io.IOException; -import javax.servlet.http.Part; - import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; +import jakarta.servlet.http.Part; + public class JavaxServletPartDeserializer extends JsonDeserializer { @Override public Part deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/JavaxServletPartSerializer.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/JavaxServletPartSerializer.java index eca1db08a88..c964241b42c 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/JavaxServletPartSerializer.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/JavaxServletPartSerializer.java @@ -19,14 +19,14 @@ import java.io.IOException; -import javax.servlet.http.Part; - import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.ObjectCodec; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.fasterxml.jackson.databind.util.TokenBuffer; +import jakarta.servlet.http.Part; + // 什么情况下用? public class JavaxServletPartSerializer extends StdSerializer { private static final long serialVersionUID = 348443113789878443L; diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/PartModule.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/PartModule.java index 7749a9534d2..5c3b8a53bd6 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/PartModule.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/PartModule.java @@ -16,12 +16,12 @@ */ package org.apache.servicecomb.foundation.common.utils.json; -import javax.servlet.http.Part; - import org.apache.servicecomb.foundation.common.utils.SPIOrder; import com.fasterxml.jackson.databind.module.SimpleModule; +import jakarta.servlet.http.Part; + public class PartModule extends SimpleModule implements SPIOrder { private static final long serialVersionUID = 4201325332650814739L; diff --git a/foundations/foundation-common/src/main/resources/META-INF/mime.types b/foundations/foundation-common/src/main/resources/META-INF/mime.types index 3e85536f857..eec38ecd549 100644 --- a/foundations/foundation-common/src/main/resources/META-INF/mime.types +++ b/foundations/foundation-common/src/main/resources/META-INF/mime.types @@ -154,4 +154,3 @@ application/x-x509-ca-cert cacert application/x-x509-server-cert scert application/x-x509-user-cert ucert application/x-x509-email-cert ecert - diff --git a/foundations/foundation-common/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.common.encrypt.Encryption b/foundations/foundation-common/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.common.encrypt.Encryption index 0e40feb3d0f..f954930aa71 100644 --- a/foundations/foundation-common/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.common.encrypt.Encryption +++ b/foundations/foundation-common/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.common.encrypt.Encryption @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.foundation.common.encrypt.NoEncryption \ No newline at end of file +org.apache.servicecomb.foundation.common.encrypt.NoEncryption diff --git a/foundations/foundation-common/src/main/resources/META-INF/spring.factories b/foundations/foundation-common/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 502ac5732c9..00000000000 --- a/foundations/foundation-common/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,20 +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. -## --------------------------------------------------------------------------- - -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.foundation.auth.FoundationCommonAuthConfiguration,\ - org.apache.servicecomb.foundation.common.event.EventManager diff --git a/foundations/foundation-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/foundations/foundation-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..de62f3b7eb0 --- /dev/null +++ b/foundations/foundation-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,19 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.foundation.auth.FoundationCommonAuthConfiguration +org.apache.servicecomb.foundation.common.event.EventManager diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventManager.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventManager.java index 6b6b1caac80..736a933159b 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventManager.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventManager.java @@ -48,7 +48,7 @@ public void normalListener() { test(this); Assertions.assertTrue(collector.getEvents().isEmpty()); // ensure no warning logs - collector.teardown(); + collector.tearDown(); } @Test @@ -72,7 +72,7 @@ void onInt(Integer obj) { Assertions.assertTrue(true); } - collector.teardown(); + collector.tearDown(); } @Test @@ -98,7 +98,7 @@ public void onInt(Integer obj) { // ensure logs: "LOGGER.warn("Failed to create lambda for method: {}, fallback to reflect.", method, throwable);" Assertions.assertFalse(collector.getEvents().isEmpty()); - collector.teardown(); + collector.tearDown(); } @Subscribe @@ -111,4 +111,3 @@ public void onInt(Integer obj) { iCount++; } } - diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpStatus.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpStatus.java index 6797c85acd2..b48ceeb4ee0 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpStatus.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpStatus.java @@ -20,8 +20,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.Response.Status.Family; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status.Family; public class TestHttpStatus { @Test diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpStatusUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpStatusUtils.java index 43a59cf4892..87776b326ef 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpStatusUtils.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpStatusUtils.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.foundation.common.http; -import javax.ws.rs.core.Response.StatusType; +import jakarta.ws.rs.core.Response.StatusType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpUtils.java index 0c56a978537..113df2905f1 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpUtils.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpUtils.java @@ -18,7 +18,7 @@ import java.net.URISyntaxException; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MediaType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -27,32 +27,24 @@ public class TestHttpUtils { @Test public void parseParamFromHeaderValue_normal() { - Assertions.assertEquals("v", HttpUtils.parseParamFromHeaderValue("xx;k=v", "k")); - } - - @Test - public void parseParamFromHeaderValue_normal_ignoreCase() { - Assertions.assertEquals("v", HttpUtils.parseParamFromHeaderValue("xx;K=v", "k")); + Assertions.assertEquals("a", HttpUtils.parseParamFromHeaderValue("key1=a;key2=b", "key1")); + Assertions.assertEquals("b", HttpUtils.parseParamFromHeaderValue("key1=a;key2= b", "key2")); + Assertions.assertEquals("b", HttpUtils.parseParamFromHeaderValue("key1=a; key2=b", "key2")); + Assertions.assertEquals("a", HttpUtils.parseParamFromHeaderValue("key1=\"a\";key2=\"b\"", "key1")); + Assertions.assertEquals("b", HttpUtils.parseParamFromHeaderValue("key1=\"a\";key2=\" b\"", "key2")); + Assertions.assertEquals("b", HttpUtils.parseParamFromHeaderValue("key1=\"a\"; key2=\"b\"", "key2")); + Assertions.assertEquals("b c.txt", HttpUtils.parseParamFromHeaderValue("key1=\"a\"; key2=\"b c.txt\"", "key2")); } @Test public void parseParamFromHeaderValue_null() { - Assertions.assertNull(HttpUtils.parseParamFromHeaderValue(null, "k")); - } - - @Test - public void parseParamFromHeaderValue_noKv() { - Assertions.assertNull(HttpUtils.parseParamFromHeaderValue("xx", "k")); - } - - @Test - public void parseParamFromHeaderValue_noV() { - Assertions.assertEquals("", HttpUtils.parseParamFromHeaderValue("xx;k=", "k")); + Assertions.assertNull(HttpUtils.parseParamFromHeaderValue(null, "key")); + Assertions.assertNull(HttpUtils.parseParamFromHeaderValue("key1=a; key2=b", "key")); } @Test - public void parseParamFromHeaderValue_keyNotFound() { - Assertions.assertNull(HttpUtils.parseParamFromHeaderValue("xx;k=", "kk")); + public void parseParamFromHeaderValue_emptyStr() { + Assertions.assertEquals("", HttpUtils.parseParamFromHeaderValue("key1=a; key2=", "key2")); } @Test diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestURIEndpointObject.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestURIEndpointObject.java index 7651a0d3965..dc3323fecc3 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestURIEndpointObject.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestURIEndpointObject.java @@ -65,8 +65,8 @@ public void testQueryChineseAndSpaceAndEmpty() throws UnsupportedEncodingExcepti "cse://1.1.1.1:1234/abc?a=1&b=&country=" + URLEncoder.encode("中 国", StandardCharsets.UTF_8.name()); URIEndpointObject ep = new URIEndpointObject(strUri); - Map> querys = Deencapsulation.getField(ep, "querys"); - Assertions.assertEquals(3, querys.size()); + Map> queries = Deencapsulation.getField(ep, "queries"); + Assertions.assertEquals(3, queries.size()); Assertions.assertEquals(1, ep.getQuery("a").size()); Assertions.assertEquals("1", ep.getFirst("a")); diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestAbstractPart.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestAbstractPart.java index de00d29d340..ae84a460e98 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestAbstractPart.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestAbstractPart.java @@ -19,7 +19,7 @@ import java.io.IOException; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MediaType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFileNameTooLong.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFileNameTooLong.java index 7ad60568d2b..0ccf049ffba 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFileNameTooLong.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFileNameTooLong.java @@ -55,15 +55,15 @@ public void assertFileNotTooLong() { Assertions.assertTrue(names.isEmpty()); } - private static void findLongFileName(File folder, List holder, int baseLenght) { + private static void findLongFileName(File folder, List holder, int baseLength) { if (folder.isFile()) { - if (folder.getAbsolutePath().length() >= MAN_FILE_SIZE + baseLenght) { + if (folder.getAbsolutePath().length() >= MAN_FILE_SIZE + baseLength) { holder.add(folder.getAbsolutePath()); } } else if (folder.isDirectory() && !"target".equals(folder.getName())) { File[] children = folder.listFiles(); for (File child : children) { - findLongFileName(child, holder, baseLenght); + findLongFileName(child, holder, baseLength); } } else { return; diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.java index 89da7bd52b3..10054efe4e8 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.java @@ -56,7 +56,7 @@ public void findMainClass_invalid() { Assertions.assertNull(JvmUtils.findMainClass()); Assertions.assertEquals("\"invalidCls\" is not a valid class.", logCollector.getEvents().get(0).getMessage()); - logCollector.teardown(); + logCollector.tearDown(); } @Test diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestRSAUtil.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestRSAUtil.java index 7c5926c7ab1..6bd9b386e36 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestRSAUtil.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestRSAUtil.java @@ -16,15 +16,30 @@ */ package org.apache.servicecomb.foundation.common.utils; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + public class TestRSAUtil { + @BeforeAll + public static void setUpClass() { + Environment environment = Mockito.mock(Environment.class); + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keyGeneratorAlgorithm", "RSA")) + .thenReturn("RSA"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.signAlgorithm", "SHA256withRSA")) + .thenReturn("SHA256withRSA"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keySize", int.class, 2048)) + .thenReturn(2048); + } @Test public void testSignVerify() diff --git a/foundations/foundation-config/pom.xml b/foundations/foundation-config/pom.xml index d2bfece9eb9..12c4a54c5be 100644 --- a/foundations/foundation-config/pom.xml +++ b/foundations/foundation-config/pom.xml @@ -21,7 +21,7 @@ foundations org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -40,13 +40,10 @@ spring-beans - com.netflix.archaius - archaius-core - - - commons-configuration - commons-configuration + org.springframework.boot + spring-boot-autoconfigure + org.yaml snakeyaml @@ -77,6 +74,12 @@ org.apache.httpcomponents httpclient + + + commons-logging + commons-logging + + io.netty diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/BootStrapProperties.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/BootStrapProperties.java index 2e882973a04..046b326b8b2 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/BootStrapProperties.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/BootStrapProperties.java @@ -17,21 +17,29 @@ package org.apache.servicecomb.config; -import java.util.LinkedHashMap; -import java.util.List; +import java.util.HashMap; import java.util.Map; -import java.util.Objects; +import java.util.Set; -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.PropertyConverter; -import org.apache.commons.configuration.SubsetConfiguration; import org.apache.commons.lang3.StringUtils; +import org.springframework.core.env.Environment; /** * This class holds configurations that need to be configured * through property files or environment variables. + * + * These properties are core model for java chassis, e.g. + *

+ *   servicecomb:
+ *     service:
+ *       application:
+ *       name:
+ *       version:
+ *       environment:
+ *       properties:
+ * 
+ * */ public class BootStrapProperties { // start of : service definition keys @@ -44,6 +52,8 @@ public class BootStrapProperties { public static final String OLD_CONFIG_SERVICE_ROLE = "service_description.role"; + public static final String OLD_CONFIG_SERVICE_ALIAS = "service_description.alias"; + public static final String OLD_CONFIG_SERVICE_DESCRIPTION = "service_description.description"; public static final String OLD_CONFIG_SERVICE_ENVIRONMENT = "service_description.environment"; @@ -52,15 +62,11 @@ public class BootStrapProperties { public static final String OLD_CONFIG_SERVICE_PROPERTIES = "service_description.properties"; - public static final String OLD_CONFIG_SERVICE_PATHS = "service_description.paths"; - // service instance definition keys of old version public static final String OLD_CONFIG_SERVICE_INSTANCE_PROPERTIES = "instance_description.properties"; public static final String OLD_CONFIG_SERVICE_INSTANCE_EXTENDED_CLASS = "instance_description.propertyExtendedClass"; - public static final String OLD_CONFIG_SERVICE_INSTANCE_ENVIRONMENT = "instance_description.environment"; - public static final String OLD_CONFIG_SERVICE_INSTANCE_INITIAL_STATUS = "instance_description.initialStatus"; // service definition keys of new version @@ -72,6 +78,8 @@ public class BootStrapProperties { public static final String CONFIG_SERVICE_ROLE = "servicecomb.service.role"; + public static final String CONFIG_SERVICE_ALIAS = "servicecomb.service.alias"; + public static final String CONFIG_SERVICE_DESCRIPTION = "servicecomb.service.description"; public static final String CONFIG_SERVICE_ENVIRONMENT = "servicecomb.service.environment"; @@ -80,15 +88,11 @@ public class BootStrapProperties { public static final String CONFIG_SERVICE_PROPERTIES = "servicecomb.service.properties"; - public static final String CONFIG_SERVICE_PATHS = "servicecomb.service.paths"; - // service instance definition keys of new version public static final String CONFIG_SERVICE_INSTANCE_PROPERTIES = "servicecomb.instance.properties"; public static final String CONFIG_SERVICE_INSTANCE_EXTENDED_CLASS = "servicecomb.instance.propertyExtendedClass"; - public static final String CONFIG_SERVICE_INSTANCE_ENVIRONMENT = "servicecomb.instance.environment"; - public static final String CONFIG_SERVICE_INSTANCE_INITIAL_STATUS = "servicecomb.instance.initialStatus"; // configuration default values @@ -102,53 +106,37 @@ public class BootStrapProperties { public static final String DEFAULT_MICROSERVICE_ENVIRONMENT = ""; - public static final String DEFAULT_MICROSERVICE_INSTANCE_ENVIRONMENT = "production"; - public static final String DEFAULT_MICROSERVICE_INSTANCE_INITIAL_STATUS = "UP"; - private static final Configuration configuration = ConfigUtil.createLocalConfig(); - - public static String readApplication(Configuration configuration) { - return readStringValue(configuration, CONFIG_SERVICE_APPLICATION, + public static String readApplication(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_APPLICATION, OLD_CONFIG_SERVICE_APPLICATION, DEFAULT_APPLICATION); } - public static String readApplication() { - return readApplication(BootStrapProperties.configuration); - } - - public static String readServiceName(Configuration configuration) { - String result = readStringValue(configuration, CONFIG_SERVICE_NAME, OLD_CONFIG_SERVICE_NAME, + public static String readServiceName(Environment environment) { + String result = readStringValue(environment, CONFIG_SERVICE_NAME, OLD_CONFIG_SERVICE_NAME, DEFAULT_MICROSERVICE_NAME); checkMicroserviceName(result); return result; } - public static String readServiceName() { - return readServiceName(BootStrapProperties.configuration); - } - - public static String readServiceVersion(Configuration configuration) { - return readStringValue(configuration, CONFIG_SERVICE_VERSION, OLD_CONFIG_SERVICE_VERSION, + public static String readServiceVersion(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_VERSION, OLD_CONFIG_SERVICE_VERSION, DEFAULT_MICROSERVICE_VERSION); } - public static String readServiceVersion() { - return readServiceVersion(BootStrapProperties.configuration); - } - - public static String readServiceRole(Configuration configuration) { - return readStringValue(configuration, CONFIG_SERVICE_ROLE, OLD_CONFIG_SERVICE_ROLE, DEFAULT_MICROSERVICE_ROLE); + public static String readServiceRole(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_ROLE, OLD_CONFIG_SERVICE_ROLE, DEFAULT_MICROSERVICE_ROLE); } - public static String readServiceRole() { - return readServiceRole(BootStrapProperties.configuration); + public static String readServiceAlias(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_ALIAS, OLD_CONFIG_SERVICE_ALIAS, null); } - public static String readServiceDescription(Configuration configuration) { - String[] descriptionArray = configuration.getStringArray(CONFIG_SERVICE_DESCRIPTION); + public static String readServiceDescription(Environment environment) { + String[] descriptionArray = environment.getProperty(CONFIG_SERVICE_DESCRIPTION, String[].class); if (null == descriptionArray || descriptionArray.length < 1) { - descriptionArray = configuration.getStringArray(OLD_CONFIG_SERVICE_DESCRIPTION); + descriptionArray = environment.getProperty(OLD_CONFIG_SERVICE_DESCRIPTION, String[].class); } if (null == descriptionArray || descriptionArray.length < 1) { @@ -163,111 +151,57 @@ public static String readServiceDescription(Configuration configuration) { return rawDescriptionBuilder.substring(0, rawDescriptionBuilder.length() - 1); } - public static String readServiceDescription() { - return readServiceDescription(BootStrapProperties.configuration); - } - - public static String readServiceEnvironment(Configuration configuration) { - return readStringValue(configuration, CONFIG_SERVICE_ENVIRONMENT, OLD_CONFIG_SERVICE_ENVIRONMENT, + public static String readServiceEnvironment(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_ENVIRONMENT, OLD_CONFIG_SERVICE_ENVIRONMENT, DEFAULT_MICROSERVICE_ENVIRONMENT); } - public static String readServiceEnvironment() { - return readServiceEnvironment(BootStrapProperties.configuration); - } - - public static String readServiceExtendedClass(Configuration configuration) { - return readStringValue(configuration, CONFIG_SERVICE_EXTENDED_CLASS, OLD_CONFIG_SERVICE_EXTENDED_CLASS, + public static String readServiceExtendedClass(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_EXTENDED_CLASS, OLD_CONFIG_SERVICE_EXTENDED_CLASS, null); } - public static String readServiceExtendedClass() { - return readServiceExtendedClass(BootStrapProperties.configuration); - } - - public static Map readServiceProperties(Configuration configuration) { - return readProperties(configuration, CONFIG_SERVICE_PROPERTIES, OLD_CONFIG_SERVICE_PROPERTIES); - } - - public static Map readServiceProperties() { - return readServiceProperties(BootStrapProperties.configuration); - } - - public static List readServicePaths(Configuration configuration) { - List result = configuration.getList(CONFIG_SERVICE_PATHS); - if (result == null || result.isEmpty()) { - result = configuration.getList(OLD_CONFIG_SERVICE_PATHS); - } - return result; - } - - public static List readServicePaths() { - return readServicePaths(BootStrapProperties.configuration); - } - - public static Map readServiceInstanceProperties(Configuration configuration) { - return readProperties(configuration, CONFIG_SERVICE_INSTANCE_PROPERTIES, OLD_CONFIG_SERVICE_INSTANCE_PROPERTIES); + public static Map readServiceProperties(Environment environment) { + return readProperties(environment, CONFIG_SERVICE_PROPERTIES, OLD_CONFIG_SERVICE_PROPERTIES); } - public static Map readServiceInstanceProperties() { - return readServiceInstanceProperties(BootStrapProperties.configuration); + public static Map readServiceInstanceProperties(Environment environment) { + return readProperties(environment, CONFIG_SERVICE_INSTANCE_PROPERTIES, OLD_CONFIG_SERVICE_INSTANCE_PROPERTIES); } - public static String readServiceInstanceExtendedClass(Configuration configuration) { - return readStringValue(configuration, CONFIG_SERVICE_INSTANCE_EXTENDED_CLASS, + public static String readServiceInstanceExtendedClass(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_INSTANCE_EXTENDED_CLASS, OLD_CONFIG_SERVICE_INSTANCE_EXTENDED_CLASS, null); } - public static String readServiceInstanceExtendedClass() { - return readServiceInstanceExtendedClass(BootStrapProperties.configuration); - } - - public static String readServiceInstanceInitialStatus(Configuration configuration) { - return readStringValue(configuration, CONFIG_SERVICE_INSTANCE_INITIAL_STATUS, + public static String readServiceInstanceInitialStatus(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_INSTANCE_INITIAL_STATUS, OLD_CONFIG_SERVICE_INSTANCE_INITIAL_STATUS, DEFAULT_MICROSERVICE_INSTANCE_INITIAL_STATUS); } - public static String readServiceInstanceInitialStatus() { - return readServiceInstanceInitialStatus(BootStrapProperties.configuration); - } - - private static String readStringValue(Configuration configuration, String newKey, String oldKey, + private static String readStringValue(Environment environment, String newKey, String oldKey, String defaultValue) { - String result = configuration.getString(newKey, null); + String result = environment.getProperty(newKey); if (result == null) { - return configuration.getString(oldKey, defaultValue); + return environment.getProperty(oldKey, defaultValue); } return result; } - private static Map readProperties(Configuration configuration, String newKey, String oldKey) { - AbstractConfiguration subset = (AbstractConfiguration) configuration.subset(newKey); - if (subset.isEmpty()) { - subset = (AbstractConfiguration) configuration.subset(oldKey); + private static Map readProperties(Environment environment, String newKey, String oldKey) { + String prefix = newKey; + Set keys = ConfigUtil.propertiesWithPrefix(environment, prefix); + if (keys.isEmpty()) { + prefix = oldKey; + keys = ConfigUtil.propertiesWithPrefix(environment, oldKey); } - return toStringMap(subset); - } - - private static Map toStringMap(AbstractConfiguration configuration) { - AbstractConfiguration root = findRoot(configuration); - Map map = new LinkedHashMap<>(); - configuration.getKeys().forEachRemaining(key -> { - Object value = configuration.getProperty(key); - // support placeholder - value = PropertyConverter.interpolate(value, root); - map.put(key, Objects.toString(value, null)); - }); - return map; - } - - private static AbstractConfiguration findRoot(AbstractConfiguration configuration) { - if (configuration instanceof SubsetConfiguration) { - return findRoot((AbstractConfiguration) ((SubsetConfiguration) configuration).getParent()); + Map result = new HashMap<>(keys.size()); + for (String key : keys) { + result.put(key.substring(prefix.length() + 1), environment.getProperty(key)); } - - return configuration; + return result; } private static void checkMicroserviceName(String name) { diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConcurrentMapConfigurationExt.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConcurrentMapConfigurationExt.java deleted file mode 100644 index 8144092286f..00000000000 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConcurrentMapConfigurationExt.java +++ /dev/null @@ -1,53 +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.servicecomb.config; - -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.commons.configuration.AbstractConfiguration; - -import com.netflix.config.ConcurrentMapConfiguration; - -/** - * Disable delimiter parsing for string - */ -@SuppressWarnings("unchecked") -public class ConcurrentMapConfigurationExt extends ConcurrentMapConfiguration { - public ConcurrentMapConfigurationExt() { - super(); - setDelimiterParsingDisabled(true); - } - - public ConcurrentMapConfigurationExt(Map mapToCopy) { - super(); - setDelimiterParsingDisabled(true); - map = new ConcurrentHashMap<>(mapToCopy); - } - - public ConcurrentMapConfigurationExt(AbstractConfiguration config) { - super(); - config.setDelimiterParsingDisabled(true); - for (Iterator i = config.getKeys(); i.hasNext(); ) { - String name = i.next(); - Object value = config.getProperty(name); - map.put(name, value); - } - } -} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigEnvironmentPostProcessor.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigEnvironmentPostProcessor.java new file mode 100644 index 00000000000..8462da9057d --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigEnvironmentPostProcessor.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.servicecomb.config.file.MicroserviceConfigLoader; +import org.apache.servicecomb.foundation.bootstrap.BootStrapService; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.env.EnvironmentPostProcessor; +import org.springframework.context.annotation.Conditional; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; + +/** + * Initialize configuration. + */ +public class ConfigEnvironmentPostProcessor implements EnvironmentPostProcessor { + public static final String MICROSERVICE_PROPERTY_SOURCE_NAME = "microservice.yaml"; + + public static final String MAPPING_PROPERTY_SOURCE_NAME = "mapping.yaml"; + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + LegacyPropertyFactory.setEnvironment(environment); + addMicroserviceDefinitions(environment); + startupBootStrapService(environment); + addDynamicConfigurationToSpring(environment); + } + + public static void addMicroserviceDefinitions(Environment environment) { + addMicroserviceYAMLToSpring(environment); + addMappingToSpring(environment); + } + + private void startupBootStrapService(Environment environment) { + for (BootStrapService bootStrapService : SPIServiceUtils.getSortedService(BootStrapService.class)) { + bootStrapService.startup(environment); + } + } + + /** + * make springboot have a change to add microservice.yaml source earlier
+ * to affect {@link Conditional} + * @param environment environment + */ + private static void addMicroserviceYAMLToSpring(Environment environment) { + if (!(environment instanceof ConfigurableEnvironment)) { + return; + } + + MutablePropertySources propertySources = ((ConfigurableEnvironment) environment).getPropertySources(); + if (propertySources.contains(MICROSERVICE_PROPERTY_SOURCE_NAME)) { + return; + } + + propertySources.addLast(new EnumerablePropertySource(MICROSERVICE_PROPERTY_SOURCE_NAME) { + private final Map values = new HashMap<>(); + + private final String[] propertyNames; + + { + MicroserviceConfigLoader loader = new MicroserviceConfigLoader(); + loader.loadAndSort(); + + loader.getConfigModels() + .forEach(configModel -> values.putAll(YAMLUtil.retrieveItems("", configModel.getConfig()))); + + propertyNames = values.keySet().toArray(new String[0]); + } + + @Override + public String[] getPropertyNames() { + return propertyNames; + } + + @SuppressWarnings("unchecked") + @Override + public Object getProperty(String name) { + Object value = this.values.get(name); + + // spring will not resolve nested placeholder of list, so try to fix the problem + if (value instanceof List) { + value = ((List) value).stream() + .filter(item -> item instanceof String) + .map(item -> environment.resolvePlaceholders((String) item)) + .collect(Collectors.toList()); + } + return value; + } + }); + } + + private static void addMappingToSpring(Environment environment) { + if (!(environment instanceof ConfigurableEnvironment)) { + return; + } + + MutablePropertySources propertySources = ((ConfigurableEnvironment) environment).getPropertySources(); + if (propertySources.contains(MAPPING_PROPERTY_SOURCE_NAME)) { + return; + } + + Map mappings = ConfigMapping.getConvertedMap(environment); + propertySources.addFirst(new MapPropertySource(MAPPING_PROPERTY_SOURCE_NAME, mappings)); + } + + private void addDynamicConfigurationToSpring(Environment environment) { + if (!(environment instanceof ConfigurableEnvironment)) { + return; + } + + for (DynamicPropertiesSource dynamicPropertiesSource : + SPIServiceUtils.getOrLoadSortedService(DynamicPropertiesSource.class)) { + ((ConfigurableEnvironment) environment).getPropertySources() + .addFirst(dynamicPropertiesSource.create(environment)); + } + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigMapping.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigMapping.java index 3063c8cdd3f..d80caaa51a9 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigMapping.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigMapping.java @@ -27,8 +27,7 @@ import java.util.List; import java.util.Map; -import org.apache.commons.configuration.Configuration; -import org.apache.servicecomb.config.archaius.sources.AbstractConfigLoader; +import org.apache.servicecomb.config.file.AbstractConfigLoader; import org.apache.servicecomb.foundation.common.utils.JvmUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -95,15 +94,6 @@ public static Map getConvertedMap(Map oldMap) { return retMap; } - public static Map getConvertedMap(Configuration config) { - if (configMap == null) { - return new LinkedHashMap<>(); - } - Map retMap = new LinkedHashMap<>(); - configMap.entrySet().forEach(entry -> putConfigsToRetMap(retMap, entry, config.getProperty(entry.getKey()))); - return retMap; - } - public static Map getConvertedMap(Environment environment) { if (configMap == null) { return new LinkedHashMap<>(); diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.java index 0a5b2685824..a09b2eaac36 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.java @@ -17,270 +17,59 @@ package org.apache.servicecomb.config; -import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_KEY_SPLITER; - +import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.stream.Collectors; - -import javax.annotation.Nonnull; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.EnvironmentConfiguration; -import org.apache.commons.configuration.PropertyConverter; -import org.apache.commons.configuration.SystemConfiguration; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.reflect.FieldUtils; -import org.apache.servicecomb.config.archaius.scheduler.NeverStartPollingScheduler; -import org.apache.servicecomb.config.archaius.sources.ConfigModel; -import org.apache.servicecomb.config.archaius.sources.MicroserviceConfigLoader; -import org.apache.servicecomb.config.archaius.sources.MicroserviceConfigurationSource; -import org.apache.servicecomb.config.event.DynamicConfigurationChangedEvent; -import org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource; -import org.apache.servicecomb.config.spi.ConfigCenterConfigurationSourceLoader; -import org.apache.servicecomb.foundation.common.event.EventManager; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.util.Set; -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.ConcurrentMapConfiguration; -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicProperty; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.WatchedUpdateListener; -import com.netflix.config.WatchedUpdateResult; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertySource; public final class ConfigUtil { - private static final Logger LOGGER = LoggerFactory.getLogger(ConfigUtil.class); - - private static final String IS_PRINT_URL = "servicecomb.config.log.verbose"; - - private static Map localConfig = new HashMap<>(); - - /** - *

The configurations not read by ServiceComb.

- *

- * For example, this map can store the configurations read by SpringBoot from application.properties, - * If users write the configurations of ServiceComb into application.yml instead of microservice.yaml, - * this can help {@link ConfigUtil} load config correctly. - *

- */ - private static final Map> EXTRA_CONFIG_MAP = new LinkedHashMap<>(); - private ConfigUtil() { } - public static void setConfigs(Map config) { - localConfig = config; - } - - public static void addConfig(String key, Object value) { - localConfig.put(key, value); - } - - public static Object getProperty(String key) { - AbstractConfiguration config = ConfigurationManager.getConfigInstance(); - return getProperty(config, key); - } - - public static Object getProperty(Object config, String key) { - if (config instanceof Configuration) { - Configuration configuration = (Configuration) config; - return configuration.getProperty(key); - } - return null; - } - - /** - * get comma separated list values from yaml string - */ - public static List getStringList(@Nonnull Configuration config, @Nonnull String key) { - return parseArrayValue(config.getString(key)).stream() - .map(v -> Objects.toString(v, null)) - .collect(Collectors.toList()); - } - public static List parseArrayValue(String value) { - return PropertyConverter.split(value, ',', true); - } - - public static ConcurrentCompositeConfiguration createLocalConfig() { - MicroserviceConfigLoader loader = new MicroserviceConfigLoader(); - loader.loadAndSort(); - if (localConfig.size() > 0) { - ConfigModel model = new ConfigModel(); - model.setConfig(localConfig); - loader.getConfigModels().add(model); + if (value == null) { + return new ArrayList<>(0); } - - boolean isPrintUrl = DynamicPropertyFactory.getInstance() - .getBooleanProperty(IS_PRINT_URL, true).get(); - if (isPrintUrl) { - LOGGER.info("create local config from paths=[{}]", StringUtils.join(loader.getConfigModels(), ",")); + String[] tokens = value.split(","); + List result = new ArrayList<>(tokens.length); + for (String t : tokens) { + result.add(t.trim()); } - - ConcurrentCompositeConfiguration config = ConfigUtil.createLocalConfig(loader.getConfigModels()); - return config; - } - - private static ConcurrentCompositeConfiguration createLocalConfig(List configModelList) { - ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration(); - - duplicateCseConfigToServicecomb(config, - new ConcurrentMapConfigurationExt(new SystemConfiguration()), - "configFromSystem"); - duplicateCseConfigToServicecomb(config, - convertEnvVariable(new ConcurrentMapConfigurationExt(new EnvironmentConfiguration())), - "configFromEnvironment"); - // If there is extra configurations, add it into config. - EXTRA_CONFIG_MAP.entrySet() - .stream() - .filter(mapEntry -> !mapEntry.getValue().isEmpty()) - .forEachOrdered(configMapEntry -> duplicateCseConfigToServicecomb(config, - new ConcurrentMapConfigurationExt(configMapEntry.getValue()), - configMapEntry.getKey())); - // we have already copy the cse config to the serviceComb config when we load the config from local yaml files - // hence, we do not need duplicate copy it. - config.addConfiguration(new DynamicConfigurationExt( - new MicroserviceConfigurationSource(configModelList), new NeverStartPollingScheduler()), - "configFromYamlFile"); - duplicateCseConfigToServicecombAtFront(config, - new ConcurrentMapConfigurationExt(ConfigMapping.getConvertedMap(config)), - "configFromMapping"); - return config; - } - - public static AbstractConfiguration convertEnvVariable(AbstractConfiguration source) { - Iterator keys = source.getKeys(); - while (keys.hasNext()) { - String key = keys.next(); - String[] separatedKey = key.split(CONFIG_KEY_SPLITER); - if (separatedKey.length == 1) { - continue; + return result; + } + + public static Set propertiesWithPrefix(Environment environment, String prefix) { + Set result = new HashSet<>(); + for (PropertySource propertySource : ((ConfigurableEnvironment) environment).getPropertySources()) { + if (propertySource instanceof EnumerablePropertySource) { + for (String key : ((EnumerablePropertySource) propertySource).getPropertyNames()) { + if (key.startsWith(prefix)) { + result.add(key); + } + } } - String newKey = String.join(".", separatedKey); - source.addProperty(newKey, source.getProperty(key)); - } - return source; - } - - private static void duplicateCseConfigToServicecomb(ConcurrentCompositeConfiguration compositeConfiguration, - AbstractConfiguration source, - String sourceName) { - - compositeConfiguration.addConfiguration(source, sourceName); - } - - private static void duplicateCseConfigToServicecombAtFront(ConcurrentCompositeConfiguration compositeConfiguration, - AbstractConfiguration source, - String sourceName) { - - compositeConfiguration.addConfigurationAtFront(source, sourceName); - } - - private static ConfigCenterConfigurationSource createConfigCenterConfigurationSource( - Configuration localConfiguration) { - ConfigCenterConfigurationSource configCenterConfigurationSource = ConfigCenterConfigurationSourceLoader - .getConfigCenterConfigurationSource(localConfiguration); - - if (null == configCenterConfigurationSource) { - LOGGER.info("none of config center source enabled."); - return null; } - - LOGGER.info("use config center source {}", configCenterConfigurationSource.getClass().getName()); - return configCenterConfigurationSource; - } - - private static void createDynamicWatchedConfiguration( - ConcurrentCompositeConfiguration localConfiguration, - ConfigCenterConfigurationSource configCenterConfigurationSource) { - ConcurrentMapConfiguration injectConfig = new ConcurrentMapConfigurationExt(); - localConfiguration.addConfigurationAtFront(injectConfig, "extraInjectConfig"); - configCenterConfigurationSource.addUpdateListener(new ServiceCombPropertyUpdateListener(injectConfig)); - - DynamicWatchedConfigurationExt configFromConfigCenter = - new DynamicWatchedConfigurationExt(configCenterConfigurationSource); - localConfiguration.addConfigurationAtFront(configFromConfigCenter, "configCenterConfig"); - } - - public static ConfigCenterConfigurationSource installDynamicConfig() { - if (ConfigurationManager.isConfigurationInstalled()) { - LOGGER.warn("Configuration installed by others, will ignore this configuration."); - return null; - } - - ConcurrentCompositeConfiguration compositeConfig = ConfigUtil.createLocalConfig(); - ConfigCenterConfigurationSource configCenterConfigurationSource = - createConfigCenterConfigurationSource(compositeConfig); - if (configCenterConfigurationSource != null) { - // add listeners - createDynamicWatchedConfiguration(compositeConfig, configCenterConfigurationSource); - // then init data - configCenterConfigurationSource.init(compositeConfig); - } - - ConfigurationManager.install(compositeConfig); - - return configCenterConfigurationSource; - } - - public static void destroyConfigCenterConfigurationSource() { - SPIServiceUtils.getAllService(ConfigCenterConfigurationSource.class).forEach(source -> { - try { - source.destroy(); - } catch (Throwable e) { - LOGGER.error("Failed to destroy {}", source.getClass().getName()); + return result; + } + + public static Map stringPropertiesWithPrefix(Environment environment, String prefix) { + Map result = new HashMap<>(); + for (PropertySource propertySource : ((ConfigurableEnvironment) environment).getPropertySources()) { + if (propertySource instanceof EnumerablePropertySource) { + for (String key : ((EnumerablePropertySource) propertySource).getPropertyNames()) { + if (key.startsWith(prefix)) { + result.put(key, environment.getProperty(key)); + } + } } - }); - } - - public static void addExtraConfig(String extraConfigName, Map extraConfig) { - EXTRA_CONFIG_MAP.put(extraConfigName, extraConfig); - } - - public static void clearExtraConfig() { - EXTRA_CONFIG_MAP.clear(); - } - - private static class ServiceCombPropertyUpdateListener implements WatchedUpdateListener { - - private final ConcurrentMapConfiguration injectConfig; - - ServiceCombPropertyUpdateListener(ConcurrentMapConfiguration injectConfig) { - this.injectConfig = injectConfig; - } - - @Override - public void updateConfiguration(WatchedUpdateResult watchedUpdateResult) { - EventManager.post(new DynamicConfigurationChangedEvent(watchedUpdateResult)); - } - } - - @SuppressWarnings("unchecked") - public static ConcurrentHashMap getAllDynamicProperties() { - try { - return (ConcurrentHashMap) FieldUtils - .readDeclaredStaticField(DynamicProperty.class, "ALL_PROPS", true); - } catch (IllegalAccessException e) { - throw new IllegalStateException(e); - } - } - - @SuppressWarnings("unchecked") - public static CopyOnWriteArraySet getCallbacks(DynamicProperty property) { - try { - return (CopyOnWriteArraySet) FieldUtils.readDeclaredField(property, "callbacks", true); - } catch (IllegalAccessException e) { - throw new IllegalStateException(e); } + return result; } } diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigurationChangedEvent.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigurationChangedEvent.java new file mode 100644 index 00000000000..2c0532a6543 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigurationChangedEvent.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.servicecomb.config; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * This event is fired when configuration changed. And the change is already applied to Environment. + * + * Listeners can use Environment to get the latest value. + */ +public class ConfigurationChangedEvent { + private final Map added; + + private final Map deleted; + + private final Map updated; + + private Set changed; + + private ConfigurationChangedEvent(Map added, Map updated, + Map deleted) { + this.added = added; + this.deleted = deleted; + this.updated = updated; + this.changed = new HashSet<>(); + this.changed.addAll(added.keySet()); + this.changed.addAll(updated.keySet()); + this.changed.addAll(deleted.keySet()); + } + + public static ConfigurationChangedEvent createIncremental(Map latest, Map last) { + Map itemsCreated = new HashMap<>(); + Map itemsDeleted = new HashMap<>(); + Map itemsModified = new HashMap<>(); + + for (Map.Entry entry : latest.entrySet()) { + String itemKey = entry.getKey(); + if (!last.containsKey(itemKey)) { + itemsCreated.put(itemKey, entry.getValue()); + } else if (!Objects.equals(last.get(itemKey), latest.get(itemKey))) { + itemsModified.put(itemKey, entry.getValue()); + } + } + for (String itemKey : last.keySet()) { + if (!latest.containsKey(itemKey)) { + itemsDeleted.put(itemKey, null); + } + } + ConfigurationChangedEvent event = ConfigurationChangedEvent + .createIncremental(itemsCreated, itemsModified, itemsDeleted); + return event; + } + + public static ConfigurationChangedEvent createIncremental(Map added, Map updated, + Map deleted) { + return new ConfigurationChangedEvent(added, updated, deleted); + } + + public static ConfigurationChangedEvent createIncremental(Map updated) { + return new ConfigurationChangedEvent(new HashMap<>(), updated, new HashMap<>()); + } + + public final Map getAdded() { + return added; + } + + + public final Map getUpdated() { + return updated; + } + + + public final Map getDeleted() { + return deleted; + } + + public final Set getChanged() { + return changed; + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DataCenterProperties.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DataCenterProperties.java new file mode 100644 index 00000000000..df61c22f547 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DataCenterProperties.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config; + +public class DataCenterProperties { + public static final String PREFIX = "servicecomb.datacenter"; + + private String name; + + private String region; + + private String availableZone; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getRegion() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + + public String getAvailableZone() { + return availableZone; + } + + public void setAvailableZone(String availableZone) { + this.availableZone = availableZone; + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicConfigurationExt.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicConfigurationExt.java deleted file mode 100644 index a89e89bf26c..00000000000 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicConfigurationExt.java +++ /dev/null @@ -1,34 +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.servicecomb.config; - -import com.netflix.config.AbstractPollingScheduler; -import com.netflix.config.DynamicConfiguration; -import com.netflix.config.PolledConfigurationSource; - -/** - * Disable delimiter parsing for string - */ -@SuppressWarnings("unchecked") -public class DynamicConfigurationExt extends DynamicConfiguration { - public DynamicConfigurationExt(PolledConfigurationSource source, AbstractPollingScheduler scheduler) { - super(); - setDelimiterParsingDisabled(true); - startPolling(source, scheduler); - } -} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicProperties.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicProperties.java index 47e060af733..58f84dd97b0 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicProperties.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicProperties.java @@ -23,10 +23,6 @@ import java.util.function.LongConsumer; public interface DynamicProperties { - - DynamicProperties DEFAULT_PROPERTY_OBSERVATION = new DynamicProperties() { - }; - default String getStringProperty(String propertyName, Consumer consumer, String defaultValue) { return defaultValue; } diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesImpl.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesImpl.java index 3931061fc7b..323e84d80aa 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesImpl.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesImpl.java @@ -17,108 +17,163 @@ package org.apache.servicecomb.config; -import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.DoubleConsumer; import java.util.function.IntConsumer; import java.util.function.LongConsumer; -import org.apache.commons.configuration.AbstractConfiguration; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.springframework.core.env.Environment; -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicBooleanProperty; -import com.netflix.config.DynamicDoubleProperty; -import com.netflix.config.DynamicFloatProperty; -import com.netflix.config.DynamicIntProperty; -import com.netflix.config.DynamicLongProperty; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicStringProperty; +import com.google.common.eventbus.Subscribe; public class DynamicPropertiesImpl implements DynamicProperties { - public DynamicPropertiesImpl() { + private static class Holder { + C callback; + + D defaultValue; + + Holder(C callback, D defaultValue) { + this.callback = callback; + this.defaultValue = defaultValue; + } + } + + private final Map, String>>> stringCallbacks = new ConcurrentHashMap<>(); + + private final Map>> intCallbacks = new ConcurrentHashMap<>(); + + private final Map>> longCallbacks = new ConcurrentHashMap<>(); + + private final Map>> floatCallbacks = new ConcurrentHashMap<>(); + + private final Map>> doubleCallbacks = new ConcurrentHashMap<>(); + + private final Map, Boolean>>> booleanCallbacks = new ConcurrentHashMap<>(); + + private final Environment environment; + + public DynamicPropertiesImpl(Environment environment) { + this.environment = environment; + EventManager.register(this); } - public DynamicPropertiesImpl(AbstractConfiguration... configurations) { - ConcurrentCompositeConfiguration configuration = new ConcurrentCompositeConfiguration(); - Arrays.stream(configurations).forEach(configuration::addConfiguration); + @Subscribe + public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { + for (Entry entry : event.getAdded().entrySet()) { + updateValue(entry); + } + + for (Entry entry : event.getUpdated().entrySet()) { + updateValue(entry); + } - ConfigurationManager.install(configuration); + for (Entry entry : event.getDeleted().entrySet()) { + updateValue(entry); + } + } + + private void updateValue(Entry entry) { + if (stringCallbacks.containsKey(entry.getKey())) { + for (Holder, String> callbacks : stringCallbacks.get(entry.getKey())) { + callbacks.callback.accept(environment.getProperty(entry.getKey(), callbacks.defaultValue)); + } + } + if (intCallbacks.containsKey(entry.getKey())) { + for (Holder callbacks : intCallbacks.get(entry.getKey())) { + callbacks.callback.accept(environment.getProperty(entry.getKey(), Integer.class, callbacks.defaultValue)); + } + } + if (longCallbacks.containsKey(entry.getKey())) { + for (Holder callbacks : longCallbacks.get(entry.getKey())) { + callbacks.callback.accept(environment.getProperty(entry.getKey(), Long.class, callbacks.defaultValue)); + } + } + if (floatCallbacks.containsKey(entry.getKey())) { + for (Holder callbacks : floatCallbacks.get(entry.getKey())) { + callbacks.callback.accept(environment.getProperty(entry.getKey(), Float.class, callbacks.defaultValue)); + } + } + if (doubleCallbacks.containsKey(entry.getKey())) { + for (Holder callbacks : doubleCallbacks.get(entry.getKey())) { + callbacks.callback.accept(environment.getProperty(entry.getKey(), Double.class, callbacks.defaultValue)); + } + } + if (booleanCallbacks.containsKey(entry.getKey())) { + for (Holder, Boolean> callbacks : booleanCallbacks.get(entry.getKey())) { + callbacks.callback.accept(environment.getProperty(entry.getKey(), Boolean.class, callbacks.defaultValue)); + } + } } @Override public String getStringProperty(String propertyName, Consumer consumer, String defaultValue) { - DynamicStringProperty prop = propertyFactoryInstance().getStringProperty(propertyName, defaultValue); - prop.addCallback(() -> consumer.accept(prop.get())); - return prop.get(); + stringCallbacks.computeIfAbsent(propertyName, key -> new HashSet<>()).add(new Holder<>(consumer, defaultValue)); + return environment.getProperty(propertyName, defaultValue); } @Override public String getStringProperty(String propertyName, String defaultValue) { - return propertyFactoryInstance().getStringProperty(propertyName, defaultValue).get(); + return environment.getProperty(propertyName, defaultValue); } @Override public int getIntProperty(String propertyName, IntConsumer consumer, int defaultValue) { - DynamicIntProperty prop = propertyFactoryInstance().getIntProperty(propertyName, defaultValue); - prop.addCallback(() -> consumer.accept(prop.get())); - return prop.get(); + intCallbacks.computeIfAbsent(propertyName, key -> new HashSet<>()).add(new Holder<>(consumer, defaultValue)); + return environment.getProperty(propertyName, int.class, defaultValue); } @Override public int getIntProperty(String propertyName, int defaultValue) { - return propertyFactoryInstance().getIntProperty(propertyName, defaultValue).get(); + return environment.getProperty(propertyName, int.class, defaultValue); } @Override public long getLongProperty(String propertyName, LongConsumer consumer, long defaultValue) { - DynamicLongProperty prop = propertyFactoryInstance().getLongProperty(propertyName, defaultValue); - prop.addCallback(() -> consumer.accept(prop.get())); - return prop.get(); + longCallbacks.computeIfAbsent(propertyName, key -> new HashSet<>()).add(new Holder<>(consumer, defaultValue)); + return environment.getProperty(propertyName, long.class, defaultValue); } @Override public long getLongProperty(String propertyName, long defaultValue) { - return propertyFactoryInstance().getLongProperty(propertyName, defaultValue).get(); + return environment.getProperty(propertyName, long.class, defaultValue); } @Override public float getFloatProperty(String propertyName, DoubleConsumer consumer, float defaultValue) { - DynamicFloatProperty prop = propertyFactoryInstance().getFloatProperty(propertyName, defaultValue); - prop.addCallback(() -> consumer.accept(prop.get())); - return prop.get(); + floatCallbacks.computeIfAbsent(propertyName, key -> new HashSet<>()).add(new Holder<>(consumer, defaultValue)); + return environment.getProperty(propertyName, float.class, defaultValue); } @Override public float getFloatProperty(String propertyName, float defaultValue) { - return propertyFactoryInstance().getFloatProperty(propertyName, defaultValue).get(); + return environment.getProperty(propertyName, float.class, defaultValue); } @Override public double getDoubleProperty(String propertyName, DoubleConsumer consumer, double defaultValue) { - DynamicDoubleProperty prop = propertyFactoryInstance().getDoubleProperty(propertyName, defaultValue); - prop.addCallback(() -> consumer.accept(prop.get())); - return prop.get(); + doubleCallbacks.computeIfAbsent(propertyName, key -> new HashSet<>()).add(new Holder<>(consumer, defaultValue)); + return environment.getProperty(propertyName, double.class, defaultValue); } @Override public double getDoubleProperty(String propertyName, double defaultValue) { - return propertyFactoryInstance().getDoubleProperty(propertyName, defaultValue).get(); + return environment.getProperty(propertyName, double.class, defaultValue); } @Override public boolean getBooleanProperty(String propertyName, Consumer consumer, boolean defaultValue) { - DynamicBooleanProperty prop = propertyFactoryInstance().getBooleanProperty(propertyName, defaultValue); - prop.addCallback(() -> consumer.accept(prop.get())); - return prop.get(); + booleanCallbacks.computeIfAbsent(propertyName, key -> new HashSet<>()).add(new Holder<>(consumer, defaultValue)); + return environment.getProperty(propertyName, boolean.class, defaultValue); } @Override public boolean getBooleanProperty(String propertyName, boolean defaultValue) { - return propertyFactoryInstance().getBooleanProperty(propertyName, defaultValue).get(); - } - - private DynamicPropertyFactory propertyFactoryInstance() { - return DynamicPropertyFactory.getInstance(); + return environment.getProperty(propertyName, boolean.class, defaultValue); } } diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesSource.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesSource.java new file mode 100644 index 00000000000..8634bff29f3 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesSource.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config; + +import org.springframework.core.Ordered; +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertySource; + +/** + * SPI to add new DynamicPropertiesSource. + */ +public interface DynamicPropertiesSource extends Ordered { + PropertySource create(Environment environment); +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicWatchedConfigurationExt.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicWatchedConfigurationExt.java deleted file mode 100644 index 0aa3b0491e5..00000000000 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicWatchedConfigurationExt.java +++ /dev/null @@ -1,75 +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.servicecomb.config; - -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.config.ConcurrentMapConfiguration; -import com.netflix.config.DynamicPropertyUpdater; -import com.netflix.config.DynamicWatchedConfiguration; -import com.netflix.config.WatchedConfigurationSource; -import com.netflix.config.WatchedUpdateListener; -import com.netflix.config.WatchedUpdateResult; - -/** - * Same as DynamicWatchedConfiguration but Disable delimiter parsing for string - * - * @see DynamicWatchedConfiguration - */ -@SuppressWarnings("unchecked") -public class DynamicWatchedConfigurationExt extends ConcurrentMapConfiguration implements WatchedUpdateListener { - - private final boolean ignoreDeletesFromSource; - - private final DynamicPropertyUpdater updater; - - private static final Logger LOGGER = LoggerFactory.getLogger(DynamicWatchedConfigurationExt.class); - - private DynamicWatchedConfigurationExt(WatchedConfigurationSource source, boolean ignoreDeletesFromSource, - DynamicPropertyUpdater updater) { - this.ignoreDeletesFromSource = ignoreDeletesFromSource; - this.updater = updater; - - setDelimiterParsingDisabled(true); - - // get a current snapshot of the config source data - try { - Map currentData = source.getCurrentData(); - WatchedUpdateResult result = WatchedUpdateResult.createFull(currentData); - - updateConfiguration(result); - } catch (final Exception exc) { - LOGGER.error("could not getCurrentData() from the WatchedConfigurationSource", exc); - } - - // add a listener for subsequent config updates - source.addUpdateListener(this); - } - - public DynamicWatchedConfigurationExt(final WatchedConfigurationSource source) { - this(source, false, new DynamicPropertyUpdater()); - } - - @Override - public void updateConfiguration(final WatchedUpdateResult result) { - updater.updateProperties(result, this, ignoreDeletesFromSource); - } -} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/FoundationConfigConfiguration.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/FoundationConfigConfiguration.java index e3651b11168..1f74e897dd6 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/FoundationConfigConfiguration.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/FoundationConfigConfiguration.java @@ -20,38 +20,50 @@ import org.apache.servicecomb.config.priority.ConfigObjectFactory; import org.apache.servicecomb.config.priority.PriorityPropertyFactory; import org.apache.servicecomb.config.priority.PriorityPropertyManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.core.env.Environment; @Configuration +@SuppressWarnings("unused") public class FoundationConfigConfiguration { @Bean - public InjectBeanPostProcessor injectBeanPostProcessor(PriorityPropertyManager priorityPropertyManager) { + public static InjectBeanPostProcessor scbInjectBeanPostProcessor( + @Autowired @Lazy PriorityPropertyManager priorityPropertyManager) { return new InjectBeanPostProcessor(priorityPropertyManager); } @Bean - public PriorityPropertyManager priorityPropertyManager(ConfigObjectFactory configObjectFactory) { + public PriorityPropertyManager scbPriorityPropertyManager(ConfigObjectFactory configObjectFactory) { return new PriorityPropertyManager(configObjectFactory); } @Bean - public PriorityPropertyFactory priorityPropertyFactory() { - return new PriorityPropertyFactory(); + public PriorityPropertyFactory scbPriorityPropertyFactory(Environment environment) { + return new PriorityPropertyFactory(environment); } @Bean - public LastPropertyPlaceholderConfigurer lastPropertyPlaceholderConfigurer() { - return new LastPropertyPlaceholderConfigurer(); + public DynamicPropertiesImpl scbDynamicProperties(Environment environment) { + return new DynamicPropertiesImpl(environment); } @Bean - public DynamicPropertiesImpl dynamicProperties() { - return new DynamicPropertiesImpl(); + public ConfigObjectFactory scbConfigObjectFactory(PriorityPropertyFactory propertyFactory) { + return new ConfigObjectFactory(propertyFactory); } @Bean - public ConfigObjectFactory configObjectFactory(PriorityPropertyFactory propertyFactory) { - return new ConfigObjectFactory(propertyFactory); + @ConfigurationProperties(prefix = DataCenterProperties.PREFIX) + public DataCenterProperties scbDataCenterProperties() { + return new DataCenterProperties(); + } + + @Bean + public InMemoryDynamicPropertiesSource scbInMemoryDynamicPropertiesSource() { + return new InMemoryDynamicPropertiesSource(); } } diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/InMemoryDynamicPropertiesSource.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/InMemoryDynamicPropertiesSource.java new file mode 100644 index 00000000000..f6139af0a24 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/InMemoryDynamicPropertiesSource.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.servicecomb.config; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +/** + * This DynamicPropertiesSource is created for easier system tests. + */ +public class InMemoryDynamicPropertiesSource implements DynamicPropertiesSource { + public static final String SOURCE_NAME = "in-memory"; + + private static final Map DYNAMIC = new HashMap<>(); + + @Override + public PropertySource create(Environment environment) { + return new MapPropertySource(SOURCE_NAME, DYNAMIC); + } + + @Override + public int getOrder() { + return -100; + } + + public static void update(String key, Object value) { + DYNAMIC.put(key, value); + + HashMap updated = new HashMap<>(); + updated.put(key, value); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); + } + + public static void reset() { + DYNAMIC.clear(); + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/LastPropertyPlaceholderConfigurer.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/LastPropertyPlaceholderConfigurer.java deleted file mode 100644 index ec5054bb716..00000000000 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/LastPropertyPlaceholderConfigurer.java +++ /dev/null @@ -1,39 +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.servicecomb.config; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.core.Ordered; - -// do not implements PriorityOrdered, must be Ordered -// because this bean must run after PropertyPlaceholderConfigurer -// this class's purpose: when asked to resolve placeholder, then throw exception directly -public class LastPropertyPlaceholderConfigurer implements BeanFactoryPostProcessor, Ordered { - @Override - public int getOrder() { - return Ordered.LOWEST_PRECEDENCE; - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - new PropertySourcesPlaceholderConfigurer().postProcessBeanFactory(beanFactory); - } -} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/YAMLUtil.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/YAMLUtil.java index a83dd03eab1..2dc116a304b 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/YAMLUtil.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/YAMLUtil.java @@ -98,13 +98,13 @@ public static T parserObject(String yamlContent, Class clazz) { } @SuppressWarnings("unchecked") - public static Map retrieveItems(String prefix, Map propertieMap) { + public static Map retrieveItems(String prefix, Map propertiesMap) { Map result = new LinkedHashMap<>(); if (!prefix.isEmpty()) { prefix += "."; } - for (Map.Entry entry : propertieMap.entrySet()) { + for (Map.Entry entry : propertiesMap.entrySet()) { if (entry.getValue() instanceof Map) { result.putAll(retrieveItems(prefix + entry.getKey(), (Map) entry.getValue())); } else { diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/scheduler/NeverStartPollingScheduler.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/scheduler/NeverStartPollingScheduler.java deleted file mode 100644 index fb52d066385..00000000000 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/scheduler/NeverStartPollingScheduler.java +++ /dev/null @@ -1,35 +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.servicecomb.config.archaius.scheduler; - -import com.netflix.config.AbstractPollingScheduler; - -/** - * Created by on 2017/3/29. - */ -public class NeverStartPollingScheduler extends AbstractPollingScheduler { - @Override - protected void schedule(Runnable runnable) { - return; - } - - @Override - public void stop() { - return; - } -} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/ConfigSourceMaker.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/ConfigSourceMaker.java deleted file mode 100644 index f822e30d985..00000000000 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/ConfigSourceMaker.java +++ /dev/null @@ -1,28 +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.servicecomb.config.archaius.sources; - -public class ConfigSourceMaker { - - public static MicroserviceConfigurationSource yamlConfigSource() { - MicroserviceConfigLoader loader = new MicroserviceConfigLoader(); - loader.loadAndSort(); - - return new MicroserviceConfigurationSource(loader.getConfigModels()); - } -} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/MicroserviceConfigurationSource.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/MicroserviceConfigurationSource.java deleted file mode 100644 index e39c987a710..00000000000 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/MicroserviceConfigurationSource.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.servicecomb.config.archaius.sources; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.apache.servicecomb.config.YAMLUtil; - -import com.netflix.config.PollResult; -import com.netflix.config.PolledConfigurationSource; - -public class MicroserviceConfigurationSource implements PolledConfigurationSource { - private final List configModels; - - public MicroserviceConfigurationSource(List configModels) { - this.configModels = configModels; - } - - public List getConfigModels() { - return configModels; - } - - public PollResult poll(boolean b, Object o) throws Exception { - Map configurations = new LinkedHashMap<>(); - - for (ConfigModel configModel : configModels) { - configurations.putAll(YAMLUtil.retrieveItems("", configModel.getConfig())); - } - - return PollResult.createFull(configurations); - } -} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/event/DynamicConfigurationChangedEvent.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/event/DynamicConfigurationChangedEvent.java deleted file mode 100644 index b466d219127..00000000000 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/event/DynamicConfigurationChangedEvent.java +++ /dev/null @@ -1,32 +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.servicecomb.config.event; - -import com.netflix.config.WatchedUpdateResult; - -public class DynamicConfigurationChangedEvent { - private final WatchedUpdateResult event; - - public DynamicConfigurationChangedEvent(WatchedUpdateResult event) { - this.event = event; - } - - public WatchedUpdateResult getEvent() { - return this.event; - } -} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/event/RefreshGovernanceConfigurationEvent.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/event/RefreshGovernanceConfigurationEvent.java deleted file mode 100644 index a3e1d799f17..00000000000 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/event/RefreshGovernanceConfigurationEvent.java +++ /dev/null @@ -1,32 +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.servicecomb.config.event; - -import com.netflix.config.WatchedUpdateResult; - -public class RefreshGovernanceConfigurationEvent { - private final WatchedUpdateResult event; - - public RefreshGovernanceConfigurationEvent(WatchedUpdateResult event) { - this.event = event; - } - - public WatchedUpdateResult getEvent() { - return this.event; - } -} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/AbstractConfigLoader.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/AbstractConfigLoader.java similarity index 98% rename from foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/AbstractConfigLoader.java rename to foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/AbstractConfigLoader.java index 74051441c14..085afc8c7e5 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/AbstractConfigLoader.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/AbstractConfigLoader.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.servicecomb.config.archaius.sources; +package org.apache.servicecomb.config.file; import java.io.IOException; import java.net.URL; diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/ConfigModel.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/ConfigModel.java similarity index 96% rename from foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/ConfigModel.java rename to foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/ConfigModel.java index bb702bf597e..41732f9ab71 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/ConfigModel.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/ConfigModel.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.servicecomb.config.archaius.sources; +package org.apache.servicecomb.config.file; import java.net.URL; import java.util.Map; diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/MicroserviceConfigLoader.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/MicroserviceConfigLoader.java similarity index 98% rename from foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/MicroserviceConfigLoader.java rename to foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/MicroserviceConfigLoader.java index e9e210255e1..f33f9cc237f 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/MicroserviceConfigLoader.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/MicroserviceConfigLoader.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.servicecomb.config.archaius.sources; +package org.apache.servicecomb.config.file; import java.io.IOException; import java.net.URL; diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/YAMLConfigLoader.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/YAMLConfigLoader.java similarity index 95% rename from foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/YAMLConfigLoader.java rename to foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/YAMLConfigLoader.java index f50451f6d86..ac69e8d4df0 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/archaius/sources/YAMLConfigLoader.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/YAMLConfigLoader.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.servicecomb.config.archaius.sources; +package org.apache.servicecomb.config.file; import java.io.IOException; import java.io.InputStream; diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/PlaceholderResolver.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/PlaceholderResolver.java index 050bcf50e32..b05ab11a73c 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/PlaceholderResolver.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/PlaceholderResolver.java @@ -25,7 +25,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; /** *
@@ -203,7 +203,7 @@ private void resolve(Row row, List resolvedRows) {
   private Object findValue(Map parameters, String key) {
     Object value = parameters.get(key);
     if (value == null) {
-      value = ConfigUtil.getProperty(key);
+      value = LegacyPropertyFactory.getStringProperty(key);
     }
     return value;
   }
diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObjectFactory.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObjectFactory.java
index aa4248f7e7b..ff18b827e39 100644
--- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObjectFactory.java
+++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObjectFactory.java
@@ -44,11 +44,11 @@
 public class ConfigObjectFactory {
   private final PriorityPropertyFactory propertyFactory;
 
-  private static final Map, JavaType> classCache = new ConcurrentHashMapEx<>();
+  private final Map, JavaType> classCache = new ConcurrentHashMapEx<>();
 
-  private static final Map javaTypeCache = new ConcurrentHashMapEx<>();
+  private final Map javaTypeCache = new ConcurrentHashMapEx<>();
 
-  private static final Map> beanDescriptionCache = new ConcurrentHashMapEx<>();
+  private final Map> beanDescriptionCache = new ConcurrentHashMapEx<>();
 
   public ConfigObjectFactory(PriorityPropertyFactory propertyFactory) {
     this.propertyFactory = propertyFactory;
@@ -100,10 +100,13 @@ public List createProperties(Object instance, String prefi
         continue;
       }
 
-      Setter setter = beanDescriptionCache.computeIfAbsent(propertyDefinition,
-          LambdaMetafactoryUtils::createObjectSetter);
       PriorityProperty priorityProperty = createPriorityProperty(propertyDefinition.getField().getAnnotated(),
           prefix, parameters);
+      if (priorityProperty == null) {
+        continue;
+      }
+      Setter setter = beanDescriptionCache.computeIfAbsent(propertyDefinition,
+          LambdaMetafactoryUtils::createObjectSetter);
       setter.set(instance, priorityProperty.getValue());
       properties.add(new ConfigObjectProperty(setter, priorityProperty));
     }
@@ -138,7 +141,7 @@ private PriorityProperty createPriorityProperty(Field field, String prefix, M
       case "java.lang.Boolean":
         return createBooleanProperty(field, keys, null);
       default:
-        throw new IllegalStateException("not support, field=" + field);
+        return null;
     }
   }
 
diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObjectProperty.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObjectProperty.java
index e81465b0f0e..be17eee8644 100644
--- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObjectProperty.java
+++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObjectProperty.java
@@ -17,8 +17,6 @@
 
 package org.apache.servicecomb.config.priority;
 
-import javax.annotation.Nullable;
-
 import org.apache.servicecomb.foundation.common.utils.bean.Setter;
 
 /**
@@ -34,8 +32,9 @@ public ConfigObjectProperty(Setter setter, PriorityProperty p
     this.property = property;
   }
 
-  public void updateValueWhenChanged(Object instance, @Nullable String changedKey) {
+  public void updateValueWhenChanged(Object instance, String changedKey) {
     if (property.isChangedKey(changedKey)) {
+      property.updateValue();
       setter.set(instance, property.getValue());
     }
   }
diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/DynamicProperty.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/DynamicProperty.java
new file mode 100644
index 00000000000..48588b6ec04
--- /dev/null
+++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/DynamicProperty.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.servicecomb.config.priority;
+
+import org.springframework.core.env.Environment;
+
+public class DynamicProperty {
+  private final Environment environment;
+
+  private final String propName;
+
+  public DynamicProperty(Environment environment, String propName) {
+    this.environment = environment;
+    this.propName = propName;
+  }
+
+  public Integer getInteger() {
+    return environment.getProperty(propName, Integer.class);
+  }
+
+  public Long getLong() {
+    return environment.getProperty(propName, Long.class);
+  }
+
+  public String getString() {
+    return environment.getProperty(propName);
+  }
+
+  public Boolean getBoolean() {
+    return environment.getProperty(propName, Boolean.class);
+  }
+
+  public Double getDouble() {
+    return environment.getProperty(propName, Double.class);
+  }
+
+  public Float getFloat() {
+    return environment.getProperty(propName, Float.class);
+  }
+
+  public String getName() {
+    return propName;
+  }
+}
diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityProperty.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityProperty.java
index 3d03267ae39..c1c30896159 100644
--- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityProperty.java
+++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityProperty.java
@@ -23,15 +23,14 @@
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
-import com.netflix.config.DynamicProperty;
+import org.springframework.core.env.Environment;
 
 /**
  * must create by PriorityPropertyManager
* or register to PriorityPropertyManager manually * @param */ -public class PriorityProperty { +public final class PriorityProperty { private static final Logger LOGGER = LoggerFactory.getLogger(PriorityProperty.class); private final PriorityPropertyType propertyType; @@ -44,19 +43,19 @@ public class PriorityProperty { private T finalValue; - public PriorityProperty(PriorityPropertyType propertyType) { + public PriorityProperty(Environment environment, PriorityPropertyType propertyType) { this.propertyType = propertyType; this.joinedPriorityKeys = Arrays.toString(propertyType.getPriorityKeys()); this.internalValueReader = collectReader(propertyType.getType()); - this.properties = createProperties(propertyType.getPriorityKeys()); + this.properties = createProperties(environment, propertyType.getPriorityKeys()); initValue(); } - private DynamicProperty[] createProperties(String[] priorityKeys) { + private DynamicProperty[] createProperties(Environment environment, String[] priorityKeys) { DynamicProperty[] properties = new DynamicProperty[priorityKeys.length]; for (int idx = 0; idx < priorityKeys.length; idx++) { String key = priorityKeys[idx].trim(); - properties[idx] = DynamicProperty.getInstance(key); + properties[idx] = new DynamicProperty(environment, key); } return properties; } @@ -90,32 +89,32 @@ private Function collectReader(Type type) { } @SuppressWarnings("unchecked") - protected T readInt(DynamicProperty property) { + private T readInt(DynamicProperty property) { return (T) property.getInteger(); } @SuppressWarnings("unchecked") - protected T readLong(DynamicProperty property) { + private T readLong(DynamicProperty property) { return (T) property.getLong(); } @SuppressWarnings("unchecked") - protected T readString(DynamicProperty property) { + private T readString(DynamicProperty property) { return (T) property.getString(); } @SuppressWarnings("unchecked") - protected T readBoolean(DynamicProperty property) { + private T readBoolean(DynamicProperty property) { return (T) property.getBoolean(); } @SuppressWarnings("unchecked") - protected T readDouble(DynamicProperty property) { + private T readDouble(DynamicProperty property) { return (T) property.getDouble(); } @SuppressWarnings("unchecked") - protected T readFloat(DynamicProperty property) { + private T readFloat(DynamicProperty property) { return (T) property.getFloat(); } diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyFactory.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyFactory.java index 0902f65ab6f..abd43f8d404 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyFactory.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyFactory.java @@ -21,51 +21,29 @@ import java.util.Map; import java.util.stream.Stream; -import javax.annotation.PreDestroy; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.commons.configuration.event.ConfigurationEvent; -import org.apache.commons.configuration.event.ConfigurationListener; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.springframework.core.env.Environment; -import com.netflix.config.ConfigurationManager; +import com.google.common.annotations.VisibleForTesting; public class PriorityPropertyFactory { - private final AbstractConfiguration configuration; - - private final ConfigurationListener configurationListener = this::configurationListener; - - // same to com.netflix.config.DynamicProperty.ALL_PROPS - // the set is finite - // will not cause OOM exception private final Map, PriorityProperty> properties = new ConcurrentHashMapEx<>(); - public PriorityPropertyFactory() { - this.configuration = ConfigurationManager.getConfigInstance(); - this.configuration.addConfigurationListener(configurationListener); - } + private final Environment environment; - @PreDestroy - public void onDestroy() { - this.configuration.removeConfigurationListener(configurationListener); + public PriorityPropertyFactory(Environment environment) { + this.environment = environment; } + @VisibleForTesting public Stream> getProperties() { return properties.values().stream(); } - private void configurationListener(ConfigurationEvent event) { - if (event.isBeforeUpdate()) { - return; - } - - // just update all properties, it's very fast, no need to do any optimize - getProperties().forEach(PriorityProperty::updateValue); - } - @SuppressWarnings("unchecked") public PriorityProperty getOrCreate(Type type, T invalidValue, T defaultValue, String... priorityKeys) { PriorityPropertyType propertyType = new PriorityPropertyType<>(type, invalidValue, defaultValue, priorityKeys); - return (PriorityProperty) properties.computeIfAbsent(propertyType, PriorityProperty::new); + return (PriorityProperty) properties.computeIfAbsent(propertyType, + key -> new PriorityProperty<>(environment, key)); } } diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyManager.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyManager.java index c1e47ac306e..8ed1e7dea2e 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyManager.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyManager.java @@ -24,17 +24,13 @@ import java.util.Map.Entry; import java.util.WeakHashMap; -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.commons.configuration.event.ConfigurationEvent; -import org.apache.commons.configuration.event.ConfigurationListener; +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.foundation.common.event.EventManager; -import com.netflix.config.ConfigurationManager; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.eventbus.Subscribe; public class PriorityPropertyManager { - private final AbstractConfiguration configuration; - - private final ConfigurationListener configurationListener = this::configurationListener; - private final ConfigObjectFactory configObjectFactory; // key is config object instance @@ -42,10 +38,8 @@ public class PriorityPropertyManager { private final Map> configObjectMap = synchronizedMap(new WeakHashMap<>()); public PriorityPropertyManager(ConfigObjectFactory configObjectFactory) { - this.configuration = ConfigurationManager.getConfigInstance(); - this.configuration.addConfigurationListener(configurationListener); - this.configObjectFactory = configObjectFactory; + EventManager.register(this); } public PriorityPropertyFactory getPropertyFactory() { @@ -53,23 +47,24 @@ public PriorityPropertyFactory getPropertyFactory() { } public void close() { - configuration.removeConfigurationListener(configurationListener); - } - public synchronized void configurationListener(ConfigurationEvent event) { - if (event.isBeforeUpdate()) { - return; - } + } + @Subscribe + public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { // just loop all properties, it's very fast, no need to do any optimize for (Entry> entry : configObjectMap.entrySet()) { Object instance = entry.getKey(); - entry.getValue() - .forEach( - configObjectProperty -> configObjectProperty.updateValueWhenChanged(instance, event.getPropertyName())); + entry.getValue().forEach( + configObjectProperty -> { + for (String updatedKey : event.getChanged()) { + configObjectProperty.updateValueWhenChanged(instance, updatedKey); + } + }); } } + @VisibleForTesting public Map> getConfigObjectMap() { return configObjectMap; } diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/spi/ConfigCenterConfigurationSource.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/spi/ConfigCenterConfigurationSource.java deleted file mode 100644 index 604e7f8b3f0..00000000000 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/spi/ConfigCenterConfigurationSource.java +++ /dev/null @@ -1,35 +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.servicecomb.config.spi; - -import org.apache.commons.configuration.Configuration; -import org.apache.servicecomb.foundation.common.utils.SPIOrder; - -import com.netflix.config.WatchedConfigurationSource; - -public interface ConfigCenterConfigurationSource extends WatchedConfigurationSource, SPIOrder { - int ORDER_BASE = 100; - - boolean isValidSource(Configuration localConfiguration); - - void init(Configuration localConfiguration); - - default void destroy() { - - } -} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/spi/ConfigCenterConfigurationSourceLoader.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/spi/ConfigCenterConfigurationSourceLoader.java deleted file mode 100644 index 2e5760e3627..00000000000 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/spi/ConfigCenterConfigurationSourceLoader.java +++ /dev/null @@ -1,42 +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.servicecomb.config.spi; - -import java.util.List; - -import org.apache.commons.configuration.Configuration; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; - -public class ConfigCenterConfigurationSourceLoader { - private static final List configCenterConfigurationSources = - SPIServiceUtils.getSortedService(ConfigCenterConfigurationSource.class); - - public static ConfigCenterConfigurationSource getConfigCenterConfigurationSource(Configuration localConfiguration) { - - ConfigCenterConfigurationSource configCenterConfigurationSource = null; - - for (ConfigCenterConfigurationSource item : configCenterConfigurationSources) { - if (item.isValidSource(localConfiguration)) { - configCenterConfigurationSource = item; - break; - } - } - - return configCenterConfigurationSource; - } -} diff --git a/foundations/foundation-config/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource b/foundations/foundation-config/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource new file mode 100644 index 00000000000..1f148e651eb --- /dev/null +++ b/foundations/foundation-config/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource @@ -0,0 +1,18 @@ +# +# 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.servicecomb.config.InMemoryDynamicPropertiesSource diff --git a/foundations/foundation-config/src/main/resources/META-INF/spring.factories b/foundations/foundation-config/src/main/resources/META-INF/spring.factories index a575e4d943e..1654b618370 100644 --- a/foundations/foundation-config/src/main/resources/META-INF/spring.factories +++ b/foundations/foundation-config/src/main/resources/META-INF/spring.factories @@ -1,19 +1,18 @@ -## --------------------------------------------------------------------------- -## 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.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.config.FoundationConfigConfiguration +# +# 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.springframework.boot.env.EnvironmentPostProcessor=\ + org.apache.servicecomb.config.ConfigEnvironmentPostProcessor diff --git a/foundations/foundation-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/foundations/foundation-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..5d39e597fdc --- /dev/null +++ b/foundations/foundation-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.config.FoundationConfigConfiguration diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/BootStrapPropertiesTest.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/BootStrapPropertiesTest.java index 7187bb8e3ff..38a8a4a8b5d 100644 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/BootStrapPropertiesTest.java +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/BootStrapPropertiesTest.java @@ -21,16 +21,28 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.Map; +import java.util.Map.Entry; -import org.apache.commons.configuration.MapConfiguration; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; -class BootStrapPropertiesTest { +public class BootStrapPropertiesTest { private Map readInstanceProperties(String yaml) { Map properties = YAMLUtil.yaml2Properties(yaml); - MapConfiguration configuration = new MapConfiguration(properties); + ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + MapPropertySource propertySource = new MapPropertySource("yaml", properties); + mutablePropertySources.addLast(propertySource); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); - return BootStrapProperties.readServiceInstanceProperties(configuration); + for (Entry entry : properties.entrySet()) { + Mockito.when(environment.getProperty(entry.getKey())).thenReturn(entry.getValue().toString()); + } + + return BootStrapProperties.readServiceInstanceProperties(environment); } @Test @@ -42,107 +54,62 @@ void should_be_empty_when_old_and_new_key_not_exists() { @Test void should_read_boolean_to_string() { - Map properties = readInstanceProperties("" - + "servicecomb:\n" - + " instance:\n" - + " properties:\n" - + " k: true"); - - assertPrettyJson(properties).isEqualTo("" - + "{\n" - + " \"k\" : \"true\"\n" - + "}"); + Map properties = readInstanceProperties(""" + servicecomb: + instance: + properties: + k: true"""); + + assertPrettyJson(properties).isEqualTo(""" + { + "k" : "true" + }"""); } @Test void should_read_number_to_string() { - Map properties = readInstanceProperties("" - + "servicecomb:\n" - + " instance:\n" - + " properties:\n" - + " k: 1"); - - assertPrettyJson(properties).isEqualTo("" - + "{\n" - + " \"k\" : \"1\"\n" - + "}"); - } - - @Test - void should_resolve_placeholder_for_instance_properties() { - Map properties = readInstanceProperties("" - + "k1: new\n" - + "servicecomb:\n" - + " instance:\n" - + " properties:\n" - + " k: ${k1}"); - - assertPrettyJson(properties).isEqualTo("" - + "{\n" - + " \"k\" : \"new\"\n" - + "}"); - } - - @Test - void should_resolve_boolean_to_string() { - Map properties = readInstanceProperties("" - + "k: true\n" - + "servicecomb:\n" - + " instance:\n" - + " properties:\n" - + " k: ${k}"); - - assertPrettyJson(properties).isEqualTo("" - + "{\n" - + " \"k\" : \"true\"\n" - + "}"); - } - - @Test - void should_resolve_number_to_string() { - Map properties = readInstanceProperties("" - + "k: 1\n" - + "servicecomb:\n" - + " instance:\n" - + " properties:\n" - + " k: ${k}"); - - assertPrettyJson(properties).isEqualTo("" - + "{\n" - + " \"k\" : \"1\"\n" - + "}"); + Map properties = readInstanceProperties(""" + servicecomb: + instance: + properties: + k: 1"""); + + assertPrettyJson(properties).isEqualTo(""" + { + "k" : "1" + }"""); } @Test void should_read_by_old_prefix_when_new_prefix_not_exists() { - Map properties = readInstanceProperties("" - + "instance_description:\n" - + " properties:\n" - + " k: v\n" - + " k1: v1"); - - assertPrettyJson(properties).isEqualTo("" - + "{\n" - + " \"k\" : \"v\",\n" - + " \"k1\" : \"v1\"\n" - + "}"); + Map properties = readInstanceProperties(""" + instance_description: + properties: + k: v + k1: v1"""); + + assertPrettyJson(properties).isEqualTo(""" + { + "k1" : "v1", + "k" : "v" + }"""); } @Test void should_ignore_old_prefix_when_new_prefix_exists() { - Map properties = readInstanceProperties("" - + "instance_description:\n" - + " properties:\n" - + " k: v\n" - + " k1: v1\n" - + "servicecomb:\n" - + " instance:\n" - + " properties:\n" - + " k: new"); - - assertPrettyJson(properties).isEqualTo("" - + "{\n" - + " \"k\" : \"new\"\n" - + "}"); + Map properties = readInstanceProperties(""" + instance_description: + properties: + k: v + k1: v1 + servicecomb: + instance: + properties: + k: new"""); + + assertPrettyJson(properties).isEqualTo(""" + { + "k" : "new" + }"""); } } diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/DynamicPropertiesTest.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/DynamicPropertiesTest.java index 9090a64ae8f..3f72af9b2cb 100644 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/DynamicPropertiesTest.java +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/DynamicPropertiesTest.java @@ -23,22 +23,20 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.number.IsCloseTo.closeTo; +import java.util.HashMap; import java.util.Objects; +import org.apache.servicecomb.foundation.common.event.EventManager; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; -import com.netflix.config.ConcurrentMapConfiguration; -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicPropertyFactory; import com.seanyinx.github.unit.scaffolding.Poller; import com.seanyinx.github.unit.scaffolding.Randomness; -import mockit.Deencapsulation; - public class DynamicPropertiesTest { - private static final String stringPropertyName = uniquify("stringPropertyName"); private static final String intPropertyName = uniquify("intPropertyName"); @@ -57,7 +55,7 @@ public class DynamicPropertiesTest { private static final long longOldValue = Randomness.nextLong(); - private static final double floatOldValue = Randomness.nextDouble(); + private static final float floatOldValue = Double.valueOf(Randomness.nextDouble()).floatValue(); private static final double doubleOldValue = Randomness.nextDouble(); @@ -65,9 +63,9 @@ public class DynamicPropertiesTest { private static final double ERROR = 0.0000001; - private static DynamicProperties dynamicProperties; + private static Environment environment; - private static ConcurrentMapConfiguration configuration; + private static DynamicProperties dynamicProperties; private final Poller poller = new Poller(2000, 100); @@ -86,26 +84,24 @@ public class DynamicPropertiesTest { @BeforeClass public static void setUpClass() throws Exception { - tearDown(); - configuration = new ConcurrentMapConfiguration(); + environment = Mockito.mock(Environment.class); writeInitialConfig(); - dynamicProperties = new DynamicPropertiesImpl(configuration); + dynamicProperties = new DynamicPropertiesImpl(environment); } @AfterClass public static void tearDown() throws Exception { - Deencapsulation.setField(ConfigurationManager.class, "instance", null); - Deencapsulation.setField(ConfigurationManager.class, "customConfigurationInstalled", false); - Deencapsulation.setField(DynamicPropertyFactory.class, "config", null); + } private static void writeInitialConfig() throws Exception { - configuration.setProperty(stringPropertyName, stringOldValue); - configuration.setProperty(intPropertyName, String.valueOf(intOldValue)); - configuration.setProperty(longPropertyName, String.valueOf(longOldValue)); - configuration.setProperty(floatPropertyName, String.valueOf(floatOldValue)); - configuration.setProperty(doublePropertyName, String.valueOf(doubleOldValue)); - configuration.setProperty(booleanPropertyName, String.valueOf(booleanOldValue)); + Mockito.when(environment.getProperty(stringPropertyName, (String) null)).thenReturn(stringOldValue); + Mockito.when(environment.getProperty(intPropertyName, int.class, 0)).thenReturn(intOldValue); + Mockito.when(environment.getProperty(longPropertyName, long.class, 0L)).thenReturn(longOldValue); + Mockito.when(environment.getProperty(floatPropertyName, float.class, 0F)).thenReturn(floatOldValue); + Mockito.when(environment.getProperty(doublePropertyName, double.class, 0D)).thenReturn(doubleOldValue); + Mockito.when(environment.getProperty(booleanPropertyName, boolean.class, false)) + .thenReturn(booleanOldValue); } @Test @@ -118,7 +114,10 @@ public void observesSpecifiedStringProperty() throws Exception { String newValue = uniquify("newValue"); - configuration.setProperty(stringPropertyName, newValue); + Mockito.when(environment.getProperty(stringPropertyName, (String) null)).thenReturn(newValue); + HashMap updated = new HashMap<>(); + updated.put(stringPropertyName, newValue); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); poller.assertEventually(() -> Objects.equals(stringPropertyValue, newValue)); } @@ -133,24 +132,37 @@ public void observesSpecifiedIntProperty() throws Exception { int newValue = Randomness.nextInt(); - configuration.setProperty(intPropertyName, String.valueOf(newValue)); + Mockito.when(environment.getProperty(intPropertyName, Integer.class, 0)).thenReturn(newValue); + HashMap updated = new HashMap<>(); + updated.put(intPropertyName, newValue); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); poller.assertEventually(() -> intPropertyValue == newValue); } @Test public void observesSpecifiedLongProperty() throws Exception { + Mockito.when(environment.getProperty(longPropertyName, long.class, 3L)).thenReturn(longOldValue); + long property = dynamicProperties.getLongProperty(longPropertyName, 0); assertThat(property, is(longOldValue)); - property = dynamicProperties.getLongProperty(longPropertyName, value -> longPropertyValue = value, 0); + property = dynamicProperties.getLongProperty(longPropertyName, value -> longPropertyValue = value, 3L); assertThat(property, is(longOldValue)); long newValue = Randomness.nextLong(); - configuration.setProperty(longPropertyName, String.valueOf(newValue)); + Mockito.when(environment.getProperty(longPropertyName, Long.class, 3L)).thenReturn(newValue); + HashMap updated = new HashMap<>(); + updated.put(longPropertyName, newValue); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); - poller.assertEventually(() -> longPropertyValue == newValue); + Mockito.when(environment.getProperty(longPropertyName, Long.class, 3L)).thenReturn(3L); + HashMap deleted = new HashMap<>(); + deleted.put(longPropertyName, newValue); + EventManager.post(ConfigurationChangedEvent.createIncremental(new HashMap<>(), new HashMap<>(), deleted)); + + poller.assertEventually(() -> longPropertyValue == 3L); } @Test @@ -158,12 +170,15 @@ public void observesSpecifiedFloatProperty() throws Exception { double property = dynamicProperties.getFloatProperty(floatPropertyName, 0); assertThat(property, closeTo(floatOldValue, ERROR)); - property = dynamicProperties.getFloatProperty(floatPropertyName, value -> floatPropertyValue = value, 0); + property = dynamicProperties.getFloatProperty(floatPropertyName, value -> floatPropertyValue = value, 0f); assertThat(property, closeTo(floatOldValue, ERROR)); - double newValue = Randomness.nextDouble(); + float newValue = Double.valueOf(Randomness.nextDouble()).floatValue(); - configuration.setProperty(floatPropertyName, String.valueOf(newValue)); + Mockito.when(environment.getProperty(floatPropertyName, Float.class, 0f)).thenReturn(newValue); + HashMap updated = new HashMap<>(); + updated.put(floatPropertyName, newValue); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); poller.assertEventually(() -> Math.abs(floatPropertyValue - newValue) < ERROR); } @@ -173,12 +188,15 @@ public void observesSpecifiedDoubleProperty() throws Exception { double property = dynamicProperties.getDoubleProperty(doublePropertyName, 0); assertThat(property, closeTo(doubleOldValue, ERROR)); - property = dynamicProperties.getDoubleProperty(doublePropertyName, value -> doublePropertyValue = value, 0); + property = dynamicProperties.getDoubleProperty(doublePropertyName, value -> doublePropertyValue = value, 0d); assertThat(property, closeTo(doubleOldValue, ERROR)); double newValue = Randomness.nextDouble(); - configuration.setProperty(doublePropertyName, String.valueOf(newValue)); + Mockito.when(environment.getProperty(doublePropertyName, Double.class, 0d)).thenReturn(newValue); + HashMap updated = new HashMap<>(); + updated.put(doublePropertyName, newValue); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); poller.assertEventually(() -> Math.abs(doublePropertyValue - newValue) < ERROR); } @@ -196,7 +214,10 @@ public void observesSpecifiedBooleanProperty() throws Exception { boolean newValue = !booleanOldValue; - configuration.setProperty(booleanPropertyName, String.valueOf(newValue)); + Mockito.when(environment.getProperty(booleanPropertyName, Boolean.class, booleanOldValue)).thenReturn(newValue); + HashMap updated = new HashMap<>(); + updated.put(booleanPropertyName, newValue); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); poller.assertEventually(() -> booleanPropertyValue == newValue); } diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/MapBasedConfigurationSource.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/MapBasedConfigurationSource.java deleted file mode 100644 index 5f08c8a8905..00000000000 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/MapBasedConfigurationSource.java +++ /dev/null @@ -1,103 +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.servicecomb.config; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.commons.configuration.Configuration; -import org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource; - -import com.netflix.config.WatchedUpdateListener; -import com.netflix.config.WatchedUpdateResult; - -public class MapBasedConfigurationSource implements ConfigCenterConfigurationSource { - - private static final Map properties = new ConcurrentHashMap<>(); - - private static final Set listeners = new HashSet<>(); - - static { - properties.put("servicecomb.abc.key", "xyz"); - } - - @Override - public boolean isValidSource(Configuration localConfiguration) { - if (localConfiguration.getProperty("cse.config.client.serverUri") == null) { - return false; - } - return true; - } - - @Override - public void init(Configuration localConfiguration) { - - } - - @Override - public void addUpdateListener(WatchedUpdateListener listener) { - listeners.add(listener); - } - - @Override - public void removeUpdateListener(WatchedUpdateListener listener) { - listeners.remove(listener); - } - - @Override - public Map getCurrentData() throws Exception { - return properties; - } - - void addProperty(String property, Object value) { - properties.put(property, value); - - Map adds = new HashMap<>(); - adds.put(property, value); - - listeners.forEach( - listener -> listener.updateConfiguration(WatchedUpdateResult.createIncremental(adds, null, null))); - } - - void setProperty(String property, Object value) { - properties.replace(property, value); - - Map changeds = new HashMap<>(); - changeds.put(property, value); - - listeners.forEach(listener -> listener - .updateConfiguration(WatchedUpdateResult.createIncremental(null, changeds, null))); - } - - void deleteProperty(String property) { - properties.remove(property); - - Map deletes = new HashMap<>(); - deletes.put(property, null); - - listeners.forEach( - listener -> listener.updateConfiguration(WatchedUpdateResult.createIncremental(null, null, deletes))); - } - - public Map getProperties() { - return properties; - } -} diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigUtil.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigUtil.java deleted file mode 100644 index 08c409f3efa..00000000000 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigUtil.java +++ /dev/null @@ -1,335 +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.servicecomb.config; - -import static com.seanyinx.github.unit.scaffolding.Randomness.uniquify; -import static org.hamcrest.CoreMatchers.equalTo; - -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.commons.configuration.Configuration; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.core.config.Configurator; -import org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource; -import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.hamcrest.MatcherAssert; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.DynamicConfiguration; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicWatchedConfiguration; - -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; - -import org.junit.jupiter.api.Assertions; - -public class TestConfigUtil { - private static final String systemPropertyName = "servicecomb.cse.servicecomb.system.setting"; - - private static final String systemExpected = uniquify("ran"); - - private static final String environmentPropertyName = "servicecomb.cse.servicecomb.environment.setting"; - - private static final String environmentExpected = uniquify("ran"); - - private final MapBasedConfigurationSource configurationSource = new MapBasedConfigurationSource(); - - @BeforeClass - public static void beforeTest() { - Configurator.setRootLevel(Level.OFF); - - ArchaiusUtils.resetConfig(); - - System.setProperty(systemPropertyName, systemExpected); - try { - setEnv(environmentPropertyName, environmentExpected); - setEnv("MY_SERVICES_ENDPOINT", "https://myhost:8888"); - } catch (Exception e) { - throw new IllegalStateException(e); - } - - ConfigUtil.installDynamicConfig(); - - Configurator.setRootLevel(Level.INFO); - } - - @AfterClass - public static void tearDown() { - ArchaiusUtils.resetConfig(); - } - - @Test - public void testArrayData() { - Configuration configuration = ConfigUtil.createLocalConfig(); - Assertions.assertEquals("a,b,c", configuration.getString("test.commonSeparatedString")); - Assertions.assertEquals(1, configuration.getStringArray("test.commonSeparatedString").length); - Assertions.assertEquals("a,b,c", configuration.getStringArray("test.commonSeparatedString")[0]); - - Assertions.assertEquals("b,c,d", configuration.getString("test.commonSeparatedStringHolder")); - Assertions.assertEquals(1, configuration.getStringArray("test.commonSeparatedStringHolder").length); - Assertions.assertEquals("b,c,d", configuration.getStringArray("test.commonSeparatedStringHolder")[0]); - - Assertions.assertEquals("m", configuration.getString("test.stringArray")); // first element - Assertions.assertEquals(2, configuration.getStringArray("test.stringArray").length); - Assertions.assertEquals("m", configuration.getStringArray("test.stringArray")[0]); - Assertions.assertEquals("n", configuration.getStringArray("test.stringArray")[1]); - } - - @Test - public void testAddConfig() { - Map config = new HashMap<>(); - config.put("service_description.name", "service_name_test"); - ConfigUtil.setConfigs(config); - ConfigUtil.addConfig("service_description.version", "1.0.2"); - ConfigUtil.addConfig("cse.test.enabled", true); - ConfigUtil.addConfig("cse.test.num", 10); - AbstractConfiguration configuration = ConfigUtil.createLocalConfig(); - Assertions.assertEquals(configuration.getString("service_description.name"), "service_name_test"); - Assertions.assertTrue(configuration.getBoolean("cse.test.enabled")); - Assertions.assertEquals(configuration.getInt("cse.test.num"), 10); - } - - @Test - public void testCreateDynamicConfigNoConfigCenterSPI() { - AbstractConfiguration dynamicConfig = ConfigUtil.createLocalConfig(); - Assertions.assertNotEquals(DynamicWatchedConfiguration.class, - ((ConcurrentCompositeConfiguration) dynamicConfig).getConfiguration(0).getClass()); - } - - @Test - public void testGetPropertyInvalidConfig() { - Assertions.assertNull(ConfigUtil.getProperty(null, "any")); - Assertions.assertNull(ConfigUtil.getProperty(new Object(), "any")); - } - - @Test - public void propertiesFromFileIsDuplicatedToCse() { - String expected = "value"; - - Assertions.assertNull(DynamicPropertyFactory - .getInstance() - .getStringProperty("cse.cse.servicecomb.file", null) - .get()); - - Assertions.assertEquals(expected, DynamicPropertyFactory - .getInstance() - .getStringProperty("servicecomb.cse.servicecomb.file", null) - .get()); - } - - @Test - public void propertiesFromSystemIsDuplicatedToCse() { - MatcherAssert.assertThat(DynamicPropertyFactory - .getInstance() - .getStringProperty(systemPropertyName, null) - .get(), - equalTo(systemExpected)); - - MatcherAssert.assertThat(DynamicPropertyFactory - .getInstance() - .getStringProperty("servicecomb.cse.servicecomb.system.setting", null) - .get(), - equalTo(systemExpected)); - } - - @Test - public void propertiesFromEnvironmentIsDuplicatedToCse() { - MatcherAssert.assertThat(DynamicPropertyFactory - .getInstance() - .getStringProperty(environmentPropertyName, null) - .get(), - equalTo(environmentExpected)); - - MatcherAssert.assertThat(DynamicPropertyFactory - .getInstance() - .getStringProperty("servicecomb.cse.servicecomb.environment.setting", null) - .get(), - equalTo(environmentExpected)); - } - - @Test - public void propertiesAddFromDynamicConfigSourceIsDuplicated() { - String expected = uniquify("ran"); - String someProperty = "cse.cse.servicecomb.add"; - - configurationSource.addProperty(someProperty, expected); - - MatcherAssert.assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(expected)); - - String changed = uniquify("changed"); - configurationSource.addProperty(someProperty, changed); - - MatcherAssert.assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(changed)); - - expected = uniquify("ran"); - someProperty = "cse.servicecomb.cse.add"; - - configurationSource.addProperty(someProperty, expected); - - MatcherAssert.assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(expected)); - - changed = uniquify("changed"); - configurationSource.addProperty(someProperty, changed); - - MatcherAssert.assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(changed)); - } - - @Test - public void propertiesChangeFromDynamicConfigSourceIsDuplicated() { - String expected = uniquify("ran"); - String someProperty = "cse.cse.servicecomb.change"; - configurationSource.addProperty(someProperty, expected); - - MatcherAssert.assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(expected)); - - String changed = uniquify("changed"); - configurationSource.setProperty(someProperty, changed); - - MatcherAssert.assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(changed)); - - expected = uniquify("ran"); - someProperty = "cse.servicecomb.cse.change"; - - configurationSource.addProperty(someProperty, expected); - MatcherAssert.assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(expected)); - - changed = uniquify("changed"); - configurationSource.setProperty(someProperty, changed); - - MatcherAssert.assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(changed)); - } - - @Test - public void propertiesDeleteFromDynamicConfigSourceIsDuplicated() { - String expected = uniquify("ran"); - String someProperty = "cse.cse.servicecomb.delete"; - - configurationSource.addProperty(someProperty, expected); - - MatcherAssert.assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(expected)); - - configurationSource.deleteProperty(someProperty); - - MatcherAssert.assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(null)); - - expected = uniquify("ran"); - someProperty = "cse.servicecomb.cse.delete"; - - configurationSource.addProperty(someProperty, expected); - - MatcherAssert.assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(expected)); - - configurationSource.deleteProperty(someProperty); - - MatcherAssert.assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(null)); - } - - @Test - public void testConvertEnvVariable() { - String someProperty = "cse_service_registry_address"; - AbstractConfiguration config = new DynamicConfiguration(); - config.addProperty(someProperty, "testing"); - AbstractConfiguration result = ConfigUtil.convertEnvVariable(config); - Assertions.assertEquals("testing", result.getString("cse.service.registry.address")); - } - - @Test - public void testCreateLocalConfigWithExtraConfig() { - Map extraConfig = new ConcurrentHashMapEx<>(1); - String extraConfigKey = "extraConfigKey"; - String extraConfigValue = "value"; - String overriddenConfigKey = "servicecomb.cse.servicecomb.file"; - extraConfig.put(extraConfigKey, extraConfigValue); - final String propertyHigherPriority = "higher_priority"; - String mapedKey1 = "servicecomb.service.mapping.address"; - String mapedKey2 = "servicecomb.service1.mapping.address"; - extraConfig.put(overriddenConfigKey, propertyHigherPriority); - - ConfigUtil.addExtraConfig("testExtraConfig", extraConfig); - - ConcurrentCompositeConfiguration localConfiguration = ConfigUtil.createLocalConfig(); - - Assertions.assertEquals(extraConfigValue, localConfiguration.getProperty(extraConfigKey)); - Assertions.assertEquals(propertyHigherPriority, localConfiguration.getString(overriddenConfigKey)); - // Test mapping key/value from self mappfing.xml - Assertions.assertEquals("https://myhost:8888", localConfiguration.getString(mapedKey1)); - Assertions.assertEquals("https://myhost:8888", localConfiguration.getString(mapedKey2)); - } - - @SuppressWarnings("unchecked") - private static void setEnv(String key, String value) throws IllegalAccessException, NoSuchFieldException { - Class[] classes = Collections.class.getDeclaredClasses(); - Map env = System.getenv(); - for (Class cl : classes) { - if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) { - Field field = cl.getDeclaredField("m"); - field.setAccessible(true); - Object obj = field.get(env); - Map map = (Map) obj; - map.put(key, value); - } - } - } - - @Test - public void destroyConfigCenterConfigurationSource() { - AtomicInteger count = new AtomicInteger(); - ConfigCenterConfigurationSource source = new MockUp() { - @Mock - void destroy() { - count.incrementAndGet(); - } - }.getMockInstance(); - - new Expectations(SPIServiceUtils.class) { - { - SPIServiceUtils.getAllService(ConfigCenterConfigurationSource.class); - result = Arrays.asList(source, source); - } - }; - - ConfigUtil.destroyConfigCenterConfigurationSource(); - - Assertions.assertEquals(2, count.get()); - } -} diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestLastPropertyPlaceholderConfigurer.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestLastPropertyPlaceholderConfigurer.java deleted file mode 100644 index fa6f3e2fe4e..00000000000 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestLastPropertyPlaceholderConfigurer.java +++ /dev/null @@ -1,69 +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.servicecomb.config; - -import java.io.IOException; -import java.util.Properties; - -import org.junit.Test; -import org.junit.jupiter.api.Assertions; -import org.springframework.context.EmbeddedValueResolverAware; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.stereotype.Component; -import org.springframework.util.StringValueResolver; - -public class TestLastPropertyPlaceholderConfigurer { - @Component - static class Bean extends PropertySourcesPlaceholderConfigurer implements EmbeddedValueResolverAware { - StringValueResolver resolver; - - public Bean() { - setIgnoreUnresolvablePlaceholders(true); - } - - @Override - public void setEmbeddedValueResolver(StringValueResolver resolver) { - this.resolver = resolver; - } - - @Override - protected Properties mergeProperties() throws IOException { - Properties properties = super.mergeProperties(); - properties.put("a", "aValue"); - return properties; - } - } - - @Test - public void test() { - AnnotationConfigApplicationContext context = - new AnnotationConfigApplicationContext(this.getClass().getPackage().getName()); - Bean bean = context.getBean(Bean.class); - - Assertions.assertEquals("aValue", bean.resolver.resolveStringValue("${a}")); - try { - bean.resolver.resolveStringValue("${b}"); - Assertions.fail("must throw exception"); - } catch (IllegalArgumentException e) { - Assertions.assertEquals("Could not resolve placeholder 'b' in value \"${b}\"", e.getMessage()); - } - - context.close(); - } -} diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java index 6694ace8644..4abafe8765e 100644 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java @@ -51,19 +51,6 @@ public void setName(String name) { public void testSafeParser() { Person person = YAMLUtil.parserObject("name: hello", Person.class); Assertions.assertEquals("hello", person.getName()); - - person = YAMLUtil.parserObject("!!org.apache.servicecomb.config.TestYAMLUtil$Person\n" - + "name: hello", Person.class); - Assertions.assertEquals("hello", person.getName()); - - person = YAMLUtil.parserObject("!!org.apache.servicecomb.config.TestYAMLUtil$UnsafePerson\n" - + "name: hello", Person.class); - Assertions.assertEquals("hello", person.getName()); - - // using Object.class is not safe, do not used in product code. - Object object = YAMLUtil.parserObject("!!org.apache.servicecomb.config.TestYAMLUtil$UnsafePerson\n" - + "name: hello", Object.class); - Assertions.assertEquals("hello", ((UnsafePerson) object).getName()); } @Test diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/archaius/sources/TestYAMLConfigurationSource.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/archaius/sources/TestYAMLConfigurationSource.java deleted file mode 100644 index 533c7e79b08..00000000000 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/archaius/sources/TestYAMLConfigurationSource.java +++ /dev/null @@ -1,98 +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.servicecomb.config.archaius.sources; - -import java.net.URL; -import java.util.List; -import java.util.Map; - -import org.apache.commons.configuration.SystemConfiguration; -import org.apache.servicecomb.config.archaius.scheduler.NeverStartPollingScheduler; -import org.junit.Test; - -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.ConcurrentMapConfiguration; -import com.netflix.config.DynamicConfiguration; -import com.netflix.config.PollResult; -import org.junit.jupiter.api.Assertions; - -/** - * Created by on 2017/1/5. - */ -public class TestYAMLConfigurationSource { - - @Test - public void testPullFromClassPath() throws Exception { - MicroserviceConfigLoader loader = new MicroserviceConfigLoader(); - loader.loadAndSort(); - - MicroserviceConfigurationSource configSource = new MicroserviceConfigurationSource(loader.getConfigModels()); - PollResult result = configSource.poll(true, null); - Map configMap = result.getComplete(); - Assertions.assertNotNull(configMap); - Assertions.assertEquals(24, configMap.size()); - Assertions.assertNotNull(configMap.get("trace.handler.sampler.percent")); - Assertions.assertEquals(0.5, configMap.get("trace.handler.sampler.percent")); - } - - @Test - public void testPullFroGivenURL() throws Exception { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - URL test1URL = loader.getResource("test1.yaml"); - URL test2URL = loader.getResource("test2.yaml"); - System.setProperty("servicecomb.configurationSource.additionalUrls", test1URL.toString() + "," + test2URL.toString()); - MicroserviceConfigLoader loader1 = new MicroserviceConfigLoader(); - loader1.loadAndSort(); - - MicroserviceConfigurationSource configSource = new MicroserviceConfigurationSource(loader1.getConfigModels()); - PollResult result = configSource.poll(true, null); - Map configMap = result.getComplete(); - - Assertions.assertEquals(3, configSource.getConfigModels().size()); - Assertions.assertNotNull(configMap); - Assertions.assertEquals(35, configMap.size()); - Assertions.assertNotNull(configMap.get("trace.handler.sampler.percent")); - Assertions.assertEquals(0.5, configMap.get("trace.handler.sampler.percent")); - - System.getProperties().remove("servicecomb.configurationSource.additionalUrls"); - } - - @Test - public void testFullOperation() { - // configuration from system properties - ConcurrentMapConfiguration configFromSystemProperties = - new ConcurrentMapConfiguration(new SystemConfiguration()); - // configuration from yaml file - MicroserviceConfigLoader loader = new MicroserviceConfigLoader(); - loader.loadAndSort(); - - DynamicConfiguration configFromYamlFile = - new DynamicConfiguration(new MicroserviceConfigurationSource(loader.getConfigModels()), new NeverStartPollingScheduler()); - // create a hierarchy of configuration that makes - // 1) dynamic configuration source override system properties - ConcurrentCompositeConfiguration finalConfig = new ConcurrentCompositeConfiguration(); - finalConfig.addConfiguration(configFromYamlFile, "yamlConfig"); - finalConfig.addConfiguration(configFromSystemProperties, "systemEnvConfig"); - Assertions.assertEquals(0.5, finalConfig.getDouble("trace.handler.sampler.percent"), 0.5); - - Object o = finalConfig.getProperty("zq"); - @SuppressWarnings("unchecked") - List> listO = (List>) o; - Assertions.assertEquals(3, listO.size()); - } -} diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/archaius/sources/TestMicroserviceConfigLoader.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/file/TestMicroserviceConfigLoader.java similarity index 98% rename from foundations/foundation-config/src/test/java/org/apache/servicecomb/config/archaius/sources/TestMicroserviceConfigLoader.java rename to foundations/foundation-config/src/test/java/org/apache/servicecomb/config/file/TestMicroserviceConfigLoader.java index c9836bf4ad1..0084976d004 100644 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/archaius/sources/TestMicroserviceConfigLoader.java +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/file/TestMicroserviceConfigLoader.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.servicecomb.config.archaius.sources; +package org.apache.servicecomb.config.file; import java.io.FileNotFoundException; import java.io.IOException; diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestConfigObjectFactory.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestConfigObjectFactory.java index b80bb60bff2..9e5915b0c20 100644 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestConfigObjectFactory.java +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestConfigObjectFactory.java @@ -17,11 +17,15 @@ package org.apache.servicecomb.config.inject; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.config.priority.TestPriorityPropertyBase; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.event.EventManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; public class TestConfigObjectFactory extends TestPriorityPropertyBase { public static class ConfigNoAnnotation { @@ -193,33 +197,33 @@ public void noAnnotation_defaultValue() { @Test public void noAnnotation_initValue() { - ArchaiusUtils.setProperty("strValue", "strValue"); - ArchaiusUtils.setProperty("strValue1", "strValue1"); - - ArchaiusUtils.setProperty("intValue", 1); - ArchaiusUtils.setProperty("intValue1", 2); - ArchaiusUtils.setProperty("intValueObj", 3); - ArchaiusUtils.setProperty("intValueObj1", 4); - - ArchaiusUtils.setProperty("longValue", 5); - ArchaiusUtils.setProperty("longValue1", 6); - ArchaiusUtils.setProperty("longValueObj", 7); - ArchaiusUtils.setProperty("longValueObj1", 8); - - ArchaiusUtils.setProperty("floatValue", 9.0); - ArchaiusUtils.setProperty("floatValue1", 10.0); - ArchaiusUtils.setProperty("floatValueObj", 11.0); - ArchaiusUtils.setProperty("floatValueObj1", 12.0); - - ArchaiusUtils.setProperty("doubleValue", 13.0); - ArchaiusUtils.setProperty("doubleValue1", 14.0); - ArchaiusUtils.setProperty("doubleValueObj", 15.0); - ArchaiusUtils.setProperty("doubleValueObj1", 16.0); - - ArchaiusUtils.setProperty("booleanValue", true); - ArchaiusUtils.setProperty("booleanValue1", true); - ArchaiusUtils.setProperty("booleanValueObj", true); - ArchaiusUtils.setProperty("booleanValueObj1", true); + Mockito.when(environment.getProperty("strValue")).thenReturn("strValue"); + Mockito.when(environment.getProperty("strValue1")).thenReturn("strValue1"); + + Mockito.when(environment.getProperty("intValue", Integer.class)).thenReturn(1); + Mockito.when(environment.getProperty("intValue1", Integer.class)).thenReturn(2); + Mockito.when(environment.getProperty("intValueObj", Integer.class)).thenReturn(3); + Mockito.when(environment.getProperty("intValueObj1", Integer.class)).thenReturn(4); + + Mockito.when(environment.getProperty("longValue", Long.class)).thenReturn(5L); + Mockito.when(environment.getProperty("longValue1", Long.class)).thenReturn(6L); + Mockito.when(environment.getProperty("longValueObj", Long.class)).thenReturn(7L); + Mockito.when(environment.getProperty("longValueObj1", Long.class)).thenReturn(8L); + + Mockito.when(environment.getProperty("floatValue", Float.class)).thenReturn(9.0F); + Mockito.when(environment.getProperty("floatValue1", Float.class)).thenReturn(10.0F); + Mockito.when(environment.getProperty("floatValueObj", Float.class)).thenReturn(11.0F); + Mockito.when(environment.getProperty("floatValueObj1", Float.class)).thenReturn(12.0F); + + Mockito.when(environment.getProperty("doubleValue", Double.class)).thenReturn(13.0D); + Mockito.when(environment.getProperty("doubleValue1", Double.class)).thenReturn(14.0D); + Mockito.when(environment.getProperty("doubleValueObj", Double.class)).thenReturn(15.0D); + Mockito.when(environment.getProperty("doubleValueObj1", Double.class)).thenReturn(16.0D); + + Mockito.when(environment.getProperty("booleanValue", Boolean.class)).thenReturn(true); + Mockito.when(environment.getProperty("booleanValue1", Boolean.class)).thenReturn(true); + Mockito.when(environment.getProperty("booleanValueObj", Boolean.class)).thenReturn(true); + Mockito.when(environment.getProperty("booleanValueObj1", Boolean.class)).thenReturn(true); ConfigNoAnnotation config = priorityPropertyManager.createConfigObject(ConfigNoAnnotation.class); @@ -256,33 +260,64 @@ public void noAnnotation_initValue() { public void noAnnotation_updateValue() { ConfigNoAnnotation config = priorityPropertyManager.createConfigObject(ConfigNoAnnotation.class); - ArchaiusUtils.setProperty("strValue", "strValue"); - ArchaiusUtils.setProperty("strValue1", "strValue1"); - - ArchaiusUtils.setProperty("intValue", 1); - ArchaiusUtils.setProperty("intValue1", 2); - ArchaiusUtils.setProperty("intValueObj", 3); - ArchaiusUtils.setProperty("intValueObj1", 4); - - ArchaiusUtils.setProperty("longValue", 5); - ArchaiusUtils.setProperty("longValue1", 6); - ArchaiusUtils.setProperty("longValueObj", 7); - ArchaiusUtils.setProperty("longValueObj1", 8); - - ArchaiusUtils.setProperty("floatValue", 9.0); - ArchaiusUtils.setProperty("floatValue1", 10.0); - ArchaiusUtils.setProperty("floatValueObj", 11.0); - ArchaiusUtils.setProperty("floatValueObj1", 12.0); - - ArchaiusUtils.setProperty("doubleValue", 13.0); - ArchaiusUtils.setProperty("doubleValue1", 14.0); - ArchaiusUtils.setProperty("doubleValueObj", 15.0); - ArchaiusUtils.setProperty("doubleValueObj1", 16.0); - - ArchaiusUtils.setProperty("booleanValue", true); - ArchaiusUtils.setProperty("booleanValue1", true); - ArchaiusUtils.setProperty("booleanValueObj", true); - ArchaiusUtils.setProperty("booleanValueObj1", true); + Mockito.when(environment.getProperty("strValue")).thenReturn("strValue"); + Mockito.when(environment.getProperty("strValue1")).thenReturn("strValue1"); + + Mockito.when(environment.getProperty("intValue", Integer.class)).thenReturn(1); + Mockito.when(environment.getProperty("intValue1", Integer.class)).thenReturn(2); + Mockito.when(environment.getProperty("intValueObj", Integer.class)).thenReturn(3); + Mockito.when(environment.getProperty("intValueObj1", Integer.class)).thenReturn(4); + + Mockito.when(environment.getProperty("longValue", Long.class)).thenReturn(5L); + Mockito.when(environment.getProperty("longValue1", Long.class)).thenReturn(6L); + Mockito.when(environment.getProperty("longValueObj", Long.class)).thenReturn(7L); + Mockito.when(environment.getProperty("longValueObj1", Long.class)).thenReturn(8L); + + Mockito.when(environment.getProperty("floatValue", Float.class)).thenReturn(9.0F); + Mockito.when(environment.getProperty("floatValue1", Float.class)).thenReturn(10.0F); + Mockito.when(environment.getProperty("floatValueObj", Float.class)).thenReturn(11.0F); + Mockito.when(environment.getProperty("floatValueObj1", Float.class)).thenReturn(12.0F); + + Mockito.when(environment.getProperty("doubleValue", Double.class)).thenReturn(13.0D); + Mockito.when(environment.getProperty("doubleValue1", Double.class)).thenReturn(14.0D); + Mockito.when(environment.getProperty("doubleValueObj", Double.class)).thenReturn(15.0D); + Mockito.when(environment.getProperty("doubleValueObj1", Double.class)).thenReturn(16.0D); + + Mockito.when(environment.getProperty("booleanValue", Boolean.class)).thenReturn(true); + Mockito.when(environment.getProperty("booleanValue1", Boolean.class)).thenReturn(true); + Mockito.when(environment.getProperty("booleanValueObj", Boolean.class)).thenReturn(true); + Mockito.when(environment.getProperty("booleanValueObj1", Boolean.class)).thenReturn(true); + + HashMap updated = new HashMap<>(); + updated.put("strValue", "strValue"); + updated.put("strValue1", "strValue1"); + + updated.put("intValue", 0); + updated.put("intValue1", 1); + updated.put("intValueObj", 2); + updated.put("intValueObj1", 3); + + updated.put("longValue", 5L); + updated.put("longValue1", 6L); + updated.put("longValueObj", 7L); + updated.put("longValueObj1", 8L); + + updated.put("floatValue", 9.0F); + updated.put("floatValue1", 10.0F); + updated.put("floatValueObj", 11.0F); + updated.put("floatValueObj1", 12.0F); + + updated.put("doubleValue", 13.0D); + updated.put("doubleValue1", 14.0D); + updated.put("doubleValueObj", 15.0D); + updated.put("doubleValueObj1", 16.0D); + + updated.put("booleanValue", true); + updated.put("booleanValue1", true); + updated.put("booleanValueObj", true); + updated.put("booleanValueObj1", true); + + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals("strValue", config.strValue); Assertions.assertEquals("strValue1", config.getStrValue1()); @@ -370,16 +405,29 @@ public void placeholder_multi_list() { Assertions.assertEquals(0, config.longValue); - ArchaiusUtils.setProperty("root.low-2.a.high-2.b", Long.MAX_VALUE); + Mockito.when(environment.getProperty("root.low-2.a.high-2.b", Long.class)).thenReturn(Long.MAX_VALUE); + HashMap updated = new HashMap<>(); + updated.put("root.low-2.a.high-2.b", Long.MAX_VALUE); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals(Long.MAX_VALUE, config.longValue); - ArchaiusUtils.setProperty("root.low-2.a.high-1.b", Long.MAX_VALUE - 1); + Mockito.when(environment.getProperty("root.low-2.a.high-1.b", Long.class)).thenReturn(Long.MAX_VALUE - 1); + updated = new HashMap<>(); + updated.put("root.low-2.a.high-1.b", Long.MAX_VALUE - 1); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); + Assertions.assertEquals(Long.MAX_VALUE - 1, config.longValue); - ArchaiusUtils.setProperty("root.low-1.a.high-2.b", Long.MAX_VALUE - 2); + Mockito.when(environment.getProperty("root.low-1.a.high-2.b", Long.class)).thenReturn(Long.MAX_VALUE - 2); + updated = new HashMap<>(); + updated.put("root.low-1.a.high-2.b", Long.MAX_VALUE - 2); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals(Long.MAX_VALUE - 2, config.longValue); - ArchaiusUtils.setProperty("root.low-1.a.high-1.b", Long.MAX_VALUE - 3); + Mockito.when(environment.getProperty("root.low-1.a.high-1.b", Long.class)).thenReturn(Long.MAX_VALUE - 3); + updated = new HashMap<>(); + updated.put("root.low-1.a.high-1.b", Long.MAX_VALUE - 3); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals(Long.MAX_VALUE - 3, config.longValue); } @@ -390,11 +438,19 @@ public void placeholder_full_list() { Assertions.assertEquals(0, config.floatValue, 0); - ArchaiusUtils.setProperty("root.l1-2", String.valueOf(1f)); - Assertions.assertEquals(1f, config.floatValue, 0); + Mockito.when(environment.getProperty("root.l1-2", Float.class)).thenReturn(1F); + HashMap updated = new HashMap<>(); + updated.put("root.l1-2", 1F); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); + + Assertions.assertEquals(1F, config.floatValue, 0); + + Mockito.when(environment.getProperty("root.l1-1", Float.class)).thenReturn(2F); + updated = new HashMap<>(); + updated.put("root.l1-1", 2F); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); - ArchaiusUtils.setProperty("root.l1-1", String.valueOf(2f)); - Assertions.assertEquals(2f, config.floatValue, 0); + Assertions.assertEquals(2F, config.floatValue, 0); } @Test @@ -403,7 +459,10 @@ public void placeholder_normal() { Assertions.assertEquals(0, config.intValue); - ArchaiusUtils.setProperty("root.k.value", "1"); + Mockito.when(environment.getProperty("root.k.value", Integer.class)).thenReturn(1); + Map updated = new HashMap<>(); + updated.put("root.k.value", 1); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals(1, config.intValue); } @@ -411,16 +470,63 @@ public void placeholder_normal() { public void overridePrefix() { ConfigWithAnnotation config = priorityPropertyManager.createConfigObject(ConfigWithAnnotation.class); - ArchaiusUtils.setProperty("override.high", "high"); + Mockito.when(environment.getProperty("override.high")).thenReturn("high"); + HashMap updated = new HashMap<>(); + updated.put("override.high", "high"); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals("high", config.strValue); - ArchaiusUtils.setProperty("override.high", null); + Mockito.when(environment.getProperty("override.high")).thenReturn(null); + updated = new HashMap<>(); + updated.put("override.high", null); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertNull(config.strValue); - ArchaiusUtils.setProperty("override.low", "low"); + Mockito.when(environment.getProperty("override.low")).thenReturn("low"); + updated = new HashMap<>(); + updated.put("override.low", "low"); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals("low", config.strValue); - ArchaiusUtils.setProperty("override.low", null); + Mockito.when(environment.getProperty("override.low")).thenReturn(null); + updated = new HashMap<>(); + updated.put("override.low", null); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertNull(config.strValue); } + + @InjectProperties(prefix = "root") + public static class ConfigWithPojo { + + @InjectProperties(prefix = "root.model") + public static class Model { + @InjectProperty(defaultValue = "h") + public String name; + } + + @InjectProperty(defaultValue = "4") + public int index; + + public Model model; + } + + @Test + public void configWithPojoWork() { + ConfigWithPojo config = priorityPropertyManager.createConfigObject(ConfigWithPojo.class); + ConfigWithPojo.Model model = priorityPropertyManager.createConfigObject(ConfigWithPojo.Model.class); + config.model = model; + + Assertions.assertEquals(4, config.index); + Assertions.assertEquals("h", config.model.name); + + Mockito.when(environment.getProperty("root.index", Integer.class)).thenReturn(5); + Mockito.when(environment.getProperty("root.model.name")).thenReturn("w"); + Map updated = new HashMap<>(); + updated.put("root.index", 5); + updated.put("root.model.name", "w"); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); + + Assertions.assertEquals(5, config.index); + Assertions.assertEquals("w", config.model.name); + } } diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestPlaceholderResolver.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestPlaceholderResolver.java index 2f7020628f4..d6407785f65 100644 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestPlaceholderResolver.java +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestPlaceholderResolver.java @@ -20,18 +20,22 @@ import java.util.HashMap; import java.util.Map; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; public class TestPlaceholderResolver { static Map parameters = new HashMap<>(); static PlaceholderResolver resolver = new PlaceholderResolver(); - @BeforeClass - public static void setup() { + @BeforeAll + public static void setupClass() { parameters.put("key", "value"); parameters.put("varOfVar", Arrays.asList("${key}")); @@ -42,6 +46,14 @@ public static void setup() { parameters.put("high-list", Arrays.asList("high-1", "high-2")); } + protected Environment environment; + + @BeforeEach + public void setup() { + environment = Mockito.mock(Environment.class); + LegacyPropertyFactory.setEnvironment(environment); + } + @Test public void unknown() { MatcherAssert.assertThat(resolver.replace("prefix${xxx}suffix", parameters), @@ -106,7 +118,8 @@ public void nested() { @Test public void mixed() { - MatcherAssert.assertThat(resolver.replace("prefix.${${priority}-list}.${key}.${high-list}.suffix ${xxx}", parameters), + MatcherAssert.assertThat( + resolver.replace("prefix.${${priority}-list}.${key}.${high-list}.suffix ${xxx}", parameters), Matchers.contains( "prefix.low-1.value.high-1.suffix ${xxx}", "prefix.low-2.value.high-1.suffix ${xxx}", diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityProperty.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityProperty.java index 4448a313ded..85b3185d251 100644 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityProperty.java +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityProperty.java @@ -16,15 +16,9 @@ */ package org.apache.servicecomb.config.priority; -import java.util.Collections; - -import org.apache.commons.configuration.MapConfiguration; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; - -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.DynamicPropertyFactory; +import org.mockito.Mockito; public class TestPriorityProperty extends TestPriorityPropertyBase { String high = "ms.schema.op"; @@ -40,26 +34,26 @@ public void testLong() { PriorityProperty config = propertyFactory.getOrCreate(Long.class, -1L, -2L, keys); Assertions.assertEquals(-2L, (long) config.getValue()); - ArchaiusUtils.setProperty(low, 1L); + updateLong(low, 1L, config); Assertions.assertEquals(1L, (long) config.getValue()); - ArchaiusUtils.setProperty(middle, 2L); + updateLong(middle, 2L, config); Assertions.assertEquals(2L, (long) config.getValue()); - ArchaiusUtils.setProperty(high, 3L); + updateLong(high, 3L, config); Assertions.assertEquals(3L, (long) config.getValue()); - ArchaiusUtils.setProperty(middle, null); + updateLong(middle, null, config); Assertions.assertEquals(3L, (long) config.getValue()); - ArchaiusUtils.setProperty(middle, 2L); - ArchaiusUtils.setProperty(high, null); + updateLong(middle, 2L, config); + updateLong(high, null, config); Assertions.assertEquals(2L, (long) config.getValue()); - ArchaiusUtils.setProperty(middle, null); + updateLong(middle, null, config); Assertions.assertEquals(1L, (long) config.getValue()); - ArchaiusUtils.setProperty(low, null); + updateLong(low, null, config); Assertions.assertEquals(-2L, (long) config.getValue()); } @@ -68,54 +62,84 @@ public void testInt() { PriorityProperty config = propertyFactory.getOrCreate(Integer.class, -1, -2, keys); Assertions.assertEquals(-2L, (int) config.getValue()); - ArchaiusUtils.setProperty(low, 1); + updateInt(low, 1, config); Assertions.assertEquals(1, (int) config.getValue()); - ArchaiusUtils.setProperty(middle, 2); + updateInt(middle, 2, config); Assertions.assertEquals(2, (int) config.getValue()); - ArchaiusUtils.setProperty(high, 3); + updateInt(high, 3, config); Assertions.assertEquals(3, (int) config.getValue()); - ArchaiusUtils.setProperty(middle, null); + updateInt(middle, null, config); Assertions.assertEquals(3, (int) config.getValue()); - ArchaiusUtils.setProperty(middle, 2); + updateInt(middle, 2, config); - ArchaiusUtils.setProperty(high, null); + updateInt(high, null, config); Assertions.assertEquals(2, (int) config.getValue()); - ArchaiusUtils.setProperty(middle, null); + updateInt(middle, null, config); Assertions.assertEquals(1, (int) config.getValue()); - ArchaiusUtils.setProperty(low, null); + updateInt(low, null, config); Assertions.assertEquals(-2, (int) config.getValue()); } + private void updateFloat(String key, Float value, PriorityProperty config) { + Mockito.when(environment.getProperty(key, Float.class)).thenReturn(value); + config.updateValue(); + } + + private void updateStr(String key, String value, PriorityProperty config) { + Mockito.when(environment.getProperty(key)).thenReturn(value); + config.updateValue(); + } + + private void updateInt(String key, Integer value, PriorityProperty config) { + Mockito.when(environment.getProperty(key, Integer.class)).thenReturn(value); + config.updateValue(); + } + + private void updateLong(String key, Long value, PriorityProperty config) { + Mockito.when(environment.getProperty(key, Long.class)).thenReturn(value); + config.updateValue(); + } + + private void updateBoolean(String key, Boolean value, PriorityProperty config) { + Mockito.when(environment.getProperty(key, Boolean.class)).thenReturn(value); + config.updateValue(); + } + + private void updateDouble(String key, Double value, PriorityProperty config) { + Mockito.when(environment.getProperty(key, Double.class)).thenReturn(value); + config.updateValue(); + } + @Test public void testString() { PriorityProperty config = propertyFactory.getOrCreate(String.class, null, "def", keys); Assertions.assertEquals("def", config.getValue()); - ArchaiusUtils.setProperty(low, 1); + updateStr(low, "1", config); Assertions.assertEquals("1", config.getValue()); - ArchaiusUtils.setProperty(middle, 2); + updateStr(middle, "2", config); Assertions.assertEquals("2", config.getValue()); - ArchaiusUtils.setProperty(high, 3); + updateStr(high, "3", config); Assertions.assertEquals("3", config.getValue()); - ArchaiusUtils.setProperty(middle, null); + updateStr(middle, null, config); Assertions.assertEquals("3", config.getValue()); - ArchaiusUtils.setProperty(middle, 2); - ArchaiusUtils.setProperty(high, null); + updateStr(middle, "2", config); + updateStr(high, null, config); Assertions.assertEquals("2", config.getValue()); - ArchaiusUtils.setProperty(middle, null); + updateStr(middle, null, config); Assertions.assertEquals("1", config.getValue()); - ArchaiusUtils.setProperty(low, null); + updateStr(low, null, config); Assertions.assertEquals("def", config.getValue()); } @@ -124,26 +148,26 @@ public void testBoolean() { PriorityProperty config = propertyFactory.getOrCreate(Boolean.class, null, false, keys); Assertions.assertFalse(config.getValue()); - ArchaiusUtils.setProperty(low, true); + updateBoolean(low, true, config); Assertions.assertTrue(config.getValue()); - ArchaiusUtils.setProperty(middle, false); + updateBoolean(middle, false, config); Assertions.assertFalse(config.getValue()); - ArchaiusUtils.setProperty(high, true); + updateBoolean(high, true, config); Assertions.assertTrue(config.getValue()); - ArchaiusUtils.setProperty(middle, false); + updateBoolean(middle, false, config); Assertions.assertTrue(config.getValue()); - ArchaiusUtils.setProperty(middle, false); - ArchaiusUtils.setProperty(high, null); + updateBoolean(middle, false, config); + updateBoolean(high, null, config); Assertions.assertFalse(config.getValue()); - ArchaiusUtils.setProperty(middle, null); + updateBoolean(middle, null, config); Assertions.assertTrue(config.getValue()); - ArchaiusUtils.setProperty(low, null); + updateBoolean(low, null, config); Assertions.assertFalse(config.getValue()); } @@ -152,26 +176,26 @@ public void testDouble() { PriorityProperty config = propertyFactory.getOrCreate(Double.class, null, -2.0, keys); Assertions.assertEquals(-2, config.getValue(), 0); - ArchaiusUtils.setProperty(low, 1); + updateDouble(low, 1D, config); Assertions.assertEquals(1, config.getValue(), 0); - ArchaiusUtils.setProperty(middle, 2); + updateDouble(middle, 2D, config); Assertions.assertEquals(2, config.getValue(), 0); - ArchaiusUtils.setProperty(high, 3); + updateDouble(high, 3D, config); Assertions.assertEquals(3, config.getValue(), 0); - ArchaiusUtils.setProperty(middle, null); + updateDouble(middle, null, config); Assertions.assertEquals(3, config.getValue(), 0); - ArchaiusUtils.setProperty(middle, 2); - ArchaiusUtils.setProperty(high, null); + updateDouble(middle, 2D, config); + updateDouble(high, null, config); Assertions.assertEquals(2, config.getValue(), 0); - ArchaiusUtils.setProperty(middle, null); + updateDouble(middle, null, config); Assertions.assertEquals(1, config.getValue(), 0); - ArchaiusUtils.setProperty(low, null); + updateDouble(low, null, config); Assertions.assertEquals(-2, config.getValue(), 0); } @@ -180,39 +204,26 @@ public void testFloat() { PriorityProperty config = propertyFactory.getOrCreate(Float.class, null, -2.0f, keys); Assertions.assertEquals(-2, config.getValue(), 0); - ArchaiusUtils.setProperty(low, 1); + updateFloat(low, 1F, config); Assertions.assertEquals(1, config.getValue(), 0); - ArchaiusUtils.setProperty(middle, 2); + updateFloat(middle, 2F, config); Assertions.assertEquals(2, config.getValue(), 0); - ArchaiusUtils.setProperty(high, 3); + updateFloat(high, 3F, config); Assertions.assertEquals(3, config.getValue(), 0); - ArchaiusUtils.setProperty(middle, null); + updateFloat(middle, null, config); Assertions.assertEquals(3, config.getValue(), 0); - ArchaiusUtils.setProperty(middle, 2); - ArchaiusUtils.setProperty(high, null); + updateFloat(middle, 2F, config); + updateFloat(high, null, config); Assertions.assertEquals(2, config.getValue(), 0); - ArchaiusUtils.setProperty(middle, null); + updateFloat(middle, null, config); Assertions.assertEquals(1, config.getValue(), 0); - ArchaiusUtils.setProperty(low, null); + updateFloat(low, null, config); Assertions.assertEquals(-2, config.getValue(), 0); } - - @Test - public void globalRefresh() { - PriorityProperty property = propertyFactory.getOrCreate(String.class, null, null, keys); - - Assertions.assertNull(property.getValue()); - - ConcurrentCompositeConfiguration config = (ConcurrentCompositeConfiguration) DynamicPropertyFactory - .getBackingConfigurationSource(); - config.addConfiguration(new MapConfiguration(Collections.singletonMap(high, "high-value"))); - - Assertions.assertEquals("high-value", property.getValue()); - } } diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyBase.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyBase.java index 4f76abbaee4..375a755ec03 100644 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyBase.java +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyBase.java @@ -16,37 +16,29 @@ */ package org.apache.servicecomb.config.priority; -import com.netflix.config.DynamicPropertyFactory; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.core.config.Configurator; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; public class TestPriorityPropertyBase { protected PriorityPropertyManager priorityPropertyManager; protected PriorityPropertyFactory propertyFactory; + protected Environment environment; + @BeforeEach public void setup() { - // avoid write too many logs - Configurator.setRootLevel(Level.OFF); - - ArchaiusUtils.resetConfig(); + environment = Mockito.mock(Environment.class); + LegacyPropertyFactory.setEnvironment(environment); - // make sure create a DynamicPropertyFactory instance - // otherwise will cause wrong order of configurationListeners - DynamicPropertyFactory.getInstance(); - - propertyFactory = new PriorityPropertyFactory(); + propertyFactory = new PriorityPropertyFactory(environment); priorityPropertyManager = new PriorityPropertyManager(new ConfigObjectFactory(propertyFactory)); - - Configurator.setRootLevel(Level.INFO); } @AfterEach public void teardown() { - ArchaiusUtils.resetConfig(); } } diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyManager.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyManager.java index 294a506dfe2..291d8614562 100644 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyManager.java +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyManager.java @@ -17,15 +17,11 @@ package org.apache.servicecomb.config.priority; -import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.config.inject.InjectProperties; import org.apache.servicecomb.config.inject.InjectProperty; -import org.apache.servicecomb.config.inject.TestConfigObjectFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import com.netflix.config.DynamicProperty; - public class TestPriorityPropertyManager extends TestPriorityPropertyBase { String high = "ms.schema.op"; @@ -37,7 +33,7 @@ public class TestPriorityPropertyManager extends TestPriorityPropertyBase { @InjectProperties(prefix = "root") public static class ConfigWithAnnotation { - @InjectProperty(prefix = "override", keys = {"high", "low"}) + @InjectProperty(prefix = "override", keys = {"high", "low"}, defaultValue = "abc") public String strValue; } @@ -65,9 +61,9 @@ public void testConfigurationsAreGCCollected() { for (int i = 0; i < 100; i++) { for (int j = 0; j < 100; j++) { - TestConfigObjectFactory.ConfigWithAnnotation configConfigObject = priorityPropertyManager.createConfigObject( - TestConfigObjectFactory.ConfigWithAnnotation.class); - Assertions.assertEquals("abc", configConfigObject.strDef); + ConfigWithAnnotation configConfigObject = priorityPropertyManager.createConfigObject( + ConfigWithAnnotation.class); + Assertions.assertEquals("abc", configConfigObject.strValue); PriorityProperty configPriorityProperty = propertyFactory.getOrCreate(Long.class, -1L, -2L, keys); Assertions.assertEquals(-2L, (long) configPriorityProperty.getValue()); } @@ -76,10 +72,6 @@ public void testConfigurationsAreGCCollected() { waitKeyForGC(priorityPropertyManager); Assertions.assertTrue(priorityPropertyManager.getConfigObjectMap().isEmpty()); - for (DynamicProperty property : ConfigUtil.getAllDynamicProperties().values()) { - Assertions.assertTrue(ConfigUtil.getCallbacks(property).isEmpty()); - } - System.out.println("Token : " + (System.currentTimeMillis() - timeBegin)); } } diff --git a/foundations/foundation-config/src/test/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource b/foundations/foundation-config/src/test/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource deleted file mode 100644 index 54abbb2cacf..00000000000 --- a/foundations/foundation-config/src/test/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.config.MapBasedConfigurationSource \ No newline at end of file diff --git a/foundations/foundation-config/src/test/resources/mapping.yaml b/foundations/foundation-config/src/test/resources/mapping.yaml index b9e0d97a3ef..66d6b0d08e1 100644 --- a/foundations/foundation-config/src/test/resources/mapping.yaml +++ b/foundations/foundation-config/src/test/resources/mapping.yaml @@ -22,4 +22,4 @@ MY_SERVICES_ENDPOINT: - servicecomb.service1.mapping.address SERVICECOMB_ENV: - service_description.environment - - service_description.environment.old \ No newline at end of file + - service_description.environment.old diff --git a/foundations/foundation-config/src/test/resources/microservice.yaml b/foundations/foundation-config/src/test/resources/microservice.yaml index d820050cfe0..43a9078fba0 100644 --- a/foundations/foundation-config/src/test/resources/microservice.yaml +++ b/foundations/foundation-config/src/test/resources/microservice.yaml @@ -60,16 +60,10 @@ cse: service_description: name: testDemo - -servicecomb: - cse: - servicecomb: - file: value # this is a config for TestConfigUtil.testCreateLocalConfigWithExtraConfig() - holder: b,c,d test: commonSeparatedString: a,b,c commonSeparatedStringHolder: ${holder} stringArray: - m - - n \ No newline at end of file + - n diff --git a/foundations/foundation-config/src/test/resources/test1.yaml b/foundations/foundation-config/src/test/resources/test1.yaml index 708d0e82f8b..e171487901f 100644 --- a/foundations/foundation-config/src/test/resources/test1.yaml +++ b/foundations/foundation-config/src/test/resources/test1.yaml @@ -38,11 +38,11 @@ shutDownHandler: enabled: true timeLimit: 30000 -hsa: +has: manager: url: 127.0.0.1 - serializer: - default: protostuff + serializer: + default: protostuff provider: service: name: ${spring.application.name} @@ -52,7 +52,7 @@ hsa: rpc: serviceLevel: interface dsf: -# ip: "10.57.65.225" +# ip: "10.57.65.225" port: 7450 eureka: diff --git a/foundations/foundation-metrics/pom.xml b/foundations/foundation-metrics/pom.xml index d63cce0d2ce..d893011c4d4 100644 --- a/foundations/foundation-metrics/pom.xml +++ b/foundations/foundation-metrics/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb foundations - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT foundation-metrics Java Chassis::Foundations::Metrics @@ -32,12 +32,8 @@ foundation-common - com.netflix.archaius - archaius-core - - - com.netflix.spectator - spectator-reg-servo + io.micrometer + micrometer-core org.apache.logging.log4j diff --git a/foundations/foundation-metrics/src/main/java/com/netflix/spectator/api/SpectatorUtils.java b/foundations/foundation-metrics/src/main/java/com/netflix/spectator/api/SpectatorUtils.java deleted file mode 100644 index cccc7107f43..00000000000 --- a/foundations/foundation-metrics/src/main/java/com/netflix/spectator/api/SpectatorUtils.java +++ /dev/null @@ -1,39 +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 com.netflix.spectator.api; - -public final class SpectatorUtils { - private SpectatorUtils() { - } - - public static Id createDefaultId(String name) { - return new DefaultId(name); - } - - public static void removeExpiredMeters(Registry registry) { - if (registry instanceof AbstractRegistry) { - ((AbstractRegistry) registry).removeExpiredMeters(); - } - } - - public static void registerMeter(Registry registry, Meter meter) { - if (!(registry instanceof AbstractRegistry)) { - throw new IllegalStateException("registry must be a AbstractRegistry, class=" + registry.getClass().getName()); - } - ((AbstractRegistry) registry).getOrCreate(meter.id(), Meter.class, null, _id -> meter); - } -} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java index 59c76af6de2..86529a36e41 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java @@ -16,41 +16,58 @@ */ package org.apache.servicecomb.foundation.metrics; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import com.google.common.eventbus.EventBus; import com.google.common.util.concurrent.ThreadFactoryBuilder; +import io.micrometer.core.instrument.MeterRegistry; + public class MetricsBootstrap { private static final Logger LOGGER = LoggerFactory.getLogger(MetricsBootstrap.class); - private GlobalRegistry globalRegistry; + private final MetricsBootstrapConfig config; - private EventBus eventBus; + private MeterRegistry meterRegistry; - private final MetricsBootstrapConfig config = new MetricsBootstrapConfig(); + private EventBus eventBus; private ScheduledExecutorService executorService; - public void start(GlobalRegistry globalRegistry, EventBus eventBus) { - this.globalRegistry = globalRegistry; + private List metricsInitializers; + + @Autowired + public MetricsBootstrap(MetricsBootstrapConfig config) { + this.config = config; + } + + @Autowired + public void setMetricsInitializers(List metricsInitializers) { + this.metricsInitializers = metricsInitializers; + } + + @Autowired + public void setMeterRegistry(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + public void start(EventBus eventBus) { this.eventBus = eventBus; this.executorService = Executors.newScheduledThreadPool(1, new ThreadFactoryBuilder() - .setNameFormat("spectator-poller-%d") + .setNameFormat("metrics-poller-%d") .build()); - loadMetricsInitializers(); + metricsInitializers.forEach(initializer -> initializer.init(this.meterRegistry, eventBus, config)); startPoll(); } @@ -59,27 +76,25 @@ public void shutdown() { executorService.shutdown(); } - List initializers = new ArrayList<>(SPIServiceUtils.getSortedService(MetricsInitializer.class)); - Collections.reverse(initializers); - initializers.forEach(MetricsInitializer::destroy); - } - - protected void loadMetricsInitializers() { - SPIServiceUtils.getSortedService(MetricsInitializer.class) - .forEach(initializer -> initializer.init(globalRegistry, eventBus, config)); + Collections.reverse(metricsInitializers); + metricsInitializers.forEach(MetricsInitializer::destroy); } protected void startPoll() { executorService.scheduleAtFixedRate(this::pollMeters, - 0, + config.getMsPollInterval(), config.getMsPollInterval(), TimeUnit.MILLISECONDS); } public synchronized void pollMeters() { + metricsInitializers.forEach(initializer -> { + if (initializer instanceof PeriodMeter) { + ((PeriodMeter) initializer).poll(System.currentTimeMillis(), config.getMsPollInterval()); + } + }); try { - long secondInterval = TimeUnit.MILLISECONDS.toSeconds(config.getMsPollInterval()); - PolledEvent polledEvent = globalRegistry.poll(secondInterval); + PolledEvent polledEvent = new PolledEvent(meterRegistry.getMeters()); eventBus.post(polledEvent); } catch (Throwable e) { LOGGER.error("poll meters error. ", e); diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrapConfig.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrapConfig.java index 09322ca3462..53ab9a6d6ff 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrapConfig.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrapConfig.java @@ -16,24 +16,52 @@ */ package org.apache.servicecomb.foundation.metrics; -import com.netflix.config.DynamicPropertyFactory; +import org.springframework.core.env.Environment; public class MetricsBootstrapConfig { public static final String METRICS_WINDOW_TIME = "servicecomb.metrics.window_time"; - public static final int DEFAULT_METRICS_WINDOW_TIME = 60000; + public static final String CONFIG_LATENCY_DISTRIBUTION = "servicecomb.metrics.invocation.latencyDistribution"; + + public static final String CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN = + "servicecomb.metrics.publisher.defaultLog.invocation.latencyDistribution.minScopeLength"; + + public static final int DEFAULT_METRICS_WINDOW_TIME = 300_000; private long msPollInterval; - public MetricsBootstrapConfig() { + private String latencyDistribution; + + private int minScopeLength; + + private Environment environment; + + public MetricsBootstrapConfig(Environment environment) { + this.environment = environment; msPollInterval = - DynamicPropertyFactory.getInstance().getIntProperty(METRICS_WINDOW_TIME, DEFAULT_METRICS_WINDOW_TIME).get(); + environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME); if (msPollInterval < 1000) { msPollInterval = 1000; } + + latencyDistribution = environment.getProperty(CONFIG_LATENCY_DISTRIBUTION, String.class); + minScopeLength = environment.getProperty( + CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7); + } + + public Environment getEnvironment() { + return environment; } public long getMsPollInterval() { return msPollInterval; } + + public String getLatencyDistribution() { + return latencyDistribution; + } + + public int getMinScopeLength() { + return minScopeLength; + } } diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.java index 88201dcb2cc..3cb0b8f95f0 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.java @@ -16,19 +16,16 @@ */ package org.apache.servicecomb.foundation.metrics; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; - import com.google.common.eventbus.EventBus; +import io.micrometer.core.instrument.MeterRegistry; + public interface MetricsInitializer { default int getOrder() { return 0; } - /** - * if create new registry, must add to globalRegistry - */ - void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config); + void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config); default void destroy() { diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/PolledEvent.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/PolledEvent.java index 6b7c3018c68..0814f96149f 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/PolledEvent.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/PolledEvent.java @@ -18,17 +18,14 @@ import java.util.List; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Meter; +import io.micrometer.core.instrument.Meter; + public class PolledEvent { private List meters; - private List measurements; - - public PolledEvent(List meters, List measurements) { + public PolledEvent(List meters) { this.meters = meters; - this.measurements = measurements; } public List getMeters() { @@ -38,12 +35,4 @@ public List getMeters() { public void setMeters(List meters) { this.meters = meters; } - - public List getMeasurements() { - return measurements; - } - - public void setMeasurements(List measurements) { - this.measurements = measurements; - } } diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthCheckResult.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthCheckResult.java index fc147fbd643..930288aa5f3 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthCheckResult.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthCheckResult.java @@ -22,7 +22,6 @@ public class HealthCheckResult { private String information; - //unsupport object or generic type,so string.. private String extraData; private long timestamp; diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/AbstractPeriodMeter.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/AbstractPeriodMeter.java deleted file mode 100644 index d9e2f46d583..00000000000 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/AbstractPeriodMeter.java +++ /dev/null @@ -1,42 +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.servicecomb.foundation.metrics.meter; - -import java.util.Collections; -import java.util.List; - -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; - -public abstract class AbstractPeriodMeter implements PeriodMeter { - protected Id id; - - protected List allMeasurements = Collections.emptyList(); - - public Id id() { - return id; - } - - @Override - public Iterable measure() { - return allMeasurements; - } - - public boolean hasExpired() { - return false; - } -} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java index 2a341cfa19b..d464697024f 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java @@ -24,6 +24,8 @@ import org.slf4j.LoggerFactory; public class LatencyDistributionConfig { + public static final Long MAX_LATENCY = 60 * 60 * 1000L; + private static final Logger LOGGER = LoggerFactory.getLogger(LatencyDistributionConfig.class); private final List scopeConfigs = new ArrayList<>(); @@ -36,7 +38,7 @@ public LatencyDistributionConfig(String config) { if (StringUtils.isEmpty(config)) { return; } - config = config.trim() + "," + Long.MAX_VALUE; + config = config.trim() + "," + MAX_LATENCY; String[] array = config.split("\\s*,+\\s*"); try { for (int idx = 0; idx < array.length - 1; idx++) { diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionMeter.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionMeter.java deleted file mode 100644 index 175c06b8eed..00000000000 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionMeter.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.servicecomb.foundation.metrics.meter; - -import java.util.ArrayList; -import java.util.List; - -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; - -public class LatencyDistributionMeter extends AbstractPeriodMeter { - private final List latencyScopeMeters = new ArrayList<>(); - - public LatencyDistributionMeter(Id id, String config) { - this.id = id; - - LatencyDistributionConfig latencyDistributionConfig = new LatencyDistributionConfig(config); - for (LatencyScopeConfig scopeConfig : latencyDistributionConfig.getScopeConfigs()) { - latencyScopeMeters.add(new LatencyScopeMeter(id, scopeConfig)); - } - } - - public void record(long nanoLatency) { - if (nanoLatency < 0) { - return; - } - - for (LatencyScopeMeter latencyScopeMeter : latencyScopeMeters) { - if (latencyScopeMeter.update(nanoLatency)) { - return; - } - } - } - - @Override - public void calcMeasurements(long msNow, long secondInterval) { - List measurements = new ArrayList<>(); - calcMeasurements(measurements, msNow, secondInterval); - allMeasurements = measurements; - } - - @Override - public void calcMeasurements(List measurements, long msNow, long secondInterval) { - latencyScopeMeters.forEach(latencyScopeMeter -> measurements.add(latencyScopeMeter.createMeasurement(msNow))); - } -} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyScopeMeter.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyScopeMeter.java deleted file mode 100644 index 114f930f084..00000000000 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyScopeMeter.java +++ /dev/null @@ -1,59 +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.servicecomb.foundation.metrics.meter; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.LongAdder; - -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; - -public class LatencyScopeMeter { - private final Id scopeId; - - private final LongAdder times = new LongAdder(); - - private long lastTimes = 0L; - - private final long nanoMin; - - private final long nanoMax; - - public LatencyScopeMeter(Id latencyDistributionId, LatencyScopeConfig config) { - nanoMin = TimeUnit.MILLISECONDS.toNanos(config.getMsMin()); - nanoMax = TimeUnit.MILLISECONDS.toNanos(config.getMsMax()); - scopeId = latencyDistributionId.withTag("scope", - String.format("[%d,%s)", config.getMsMin(), config.getMsMax() == Long.MAX_VALUE ? "" : config.getMsMax())); - } - - public boolean update(long nanoLatency) { - if (nanoMin <= nanoLatency && nanoMax > nanoLatency) { - times.increment(); - return true; - } - - return false; - } - - public Measurement createMeasurement(long msNow) { - long currentTimes = times.longValue(); - long deltaTimes = currentTimes - lastTimes; - this.lastTimes = currentTimes; - - return new Measurement(scopeId, msNow, deltaTimes); - } -} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/PeriodMeter.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/PeriodMeter.java index f91d781e17a..a846d08179d 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/PeriodMeter.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/PeriodMeter.java @@ -16,13 +16,6 @@ */ package org.apache.servicecomb.foundation.metrics.meter; -import java.util.List; - -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Meter; - -public interface PeriodMeter extends Meter { - void calcMeasurements(long msNow, long secondInterval); - - void calcMeasurements(List measurements, long msNow, long secondInterval); +public interface PeriodMeter { + void poll(long msNow, long secondInterval); } diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/SimpleTimer.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/SimpleTimer.java deleted file mode 100644 index 09803a4718d..00000000000 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/SimpleTimer.java +++ /dev/null @@ -1,93 +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.servicecomb.foundation.metrics.meter; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.LongAdder; - -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Statistic; -import com.netflix.spectator.impl.AtomicDouble; - -/** - * ServoTimer is too slow - * this is a faster timer - */ -public class SimpleTimer extends AbstractPeriodMeter { - private static final double CNV_SECONDS = 1.0 / TimeUnit.SECONDS.toNanos(1L); - - private final Id idCount; - - private final Id idTotalTime; - - private final Id idMax; - - private final LongAdder count = new LongAdder(); - - private final LongAdder totalTime = new LongAdder(); - - private final AtomicDouble max = new AtomicDouble(); - - private long lastCount = 0; - - private long lastTotalTime = 0; - - public SimpleTimer(Id id) { - this.id = id; - this.idCount = id.withTag(Statistic.count); - this.idTotalTime = id.withTag(Statistic.totalTime); - this.idMax = id.withTag(Statistic.max); - } - - public void record(long nanoAmount) { - if (nanoAmount >= 0) { - totalTime.add(nanoAmount); - count.increment(); - max.max(nanoAmount); - } - } - - private Measurement newMeasurement(Id id, long msNow, Number n) { - return new Measurement(id, msNow, n.doubleValue()); - } - - @Override - public void calcMeasurements(long msNow, long secondInterval) { - List measurements = new ArrayList<>(3); - calcMeasurements(measurements, msNow, secondInterval); - allMeasurements = measurements; - } - - @Override - public void calcMeasurements(List measurements, long msNow, long secondInterval) { - long currentCount = count.longValue(); - long currentTotalTime = totalTime.longValue(); - - measurements.add(newMeasurement(idCount, msNow, (double) (currentCount - lastCount) / secondInterval)); - measurements - .add(newMeasurement(idTotalTime, msNow, (currentTotalTime - lastTotalTime) / secondInterval * CNV_SECONDS)); - measurements.add(newMeasurement(idMax, msNow, max.get() * CNV_SECONDS)); - - lastCount = currentCount; - lastTotalTime = currentTotalTime; - // maybe lost some max value, but not so important? - max.set(0); - } -} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/DefaultTagFinder.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/DefaultTagFinder.java similarity index 90% rename from foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/DefaultTagFinder.java rename to foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/DefaultTagFinder.java index 17d3d9a7370..f29227c6a72 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/DefaultTagFinder.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/DefaultTagFinder.java @@ -14,9 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.foundation.metrics.publish.spectator; +package org.apache.servicecomb.foundation.metrics.publish; -import com.netflix.spectator.api.Tag; + +import io.micrometer.core.instrument.Tag; public class DefaultTagFinder implements TagFinder { private final String tagKey; @@ -45,7 +46,7 @@ public String getTagKey() { @Override public Tag find(Iterable tags) { for (Tag tag : tags) { - if (tag.key().equals(tagKey)) { + if (tag.getKey().equals(tagKey)) { return tag; } } diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementGroupConfig.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementGroupConfig.java similarity index 95% rename from foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementGroupConfig.java rename to foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementGroupConfig.java index 0942baf2666..cec6db25eb4 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementGroupConfig.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementGroupConfig.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.foundation.metrics.publish.spectator; +package org.apache.servicecomb.foundation.metrics.publish; import java.util.Arrays; import java.util.HashMap; diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementNode.java similarity index 79% rename from foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java rename to foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementNode.java index c138bea15eb..8ff19a15345 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementNode.java @@ -14,29 +14,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.foundation.metrics.publish.spectator; +package org.apache.servicecomb.foundation.metrics.publish; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import com.netflix.spectator.api.Measurement; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter.Id; public class MeasurementNode implements Comparable { private final String name; + private final Id id; + private final List measurements = new ArrayList<>(); private Map children; - public MeasurementNode(String name, Map children) { + public MeasurementNode(String name, Id id, Map children) { this.name = name; + this.id = id; this.children = children; } public String getName() { - return name; + return this.name; + } + + public Id getId() { + return id; } public Map getChildren() { @@ -62,13 +70,16 @@ public MeasurementNode findChild(String... childNames) { return node; } - public MeasurementNode addChild(String childName, Measurement measurement) { + public MeasurementNode addChild(String childName, Id id, Measurement measurement) { if (children == null) { children = new LinkedHashMap<>(); } - MeasurementNode node = children.computeIfAbsent(childName, name -> new MeasurementNode(name, null)); - node.addMeasurement(measurement); + MeasurementNode node = children.computeIfAbsent(childName, name -> new MeasurementNode(name, id, null)); + + if (measurement != null) { + node.addMeasurement(measurement); + } return node; } @@ -84,7 +95,7 @@ public void addMeasurement(Measurement measurement) { public double summary() { double result = 0; for (Measurement measurement : measurements) { - result += measurement.value(); + result += measurement.getValue(); } return result; diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementTree.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementTree.java new file mode 100644 index 00000000000..a03c4739ef7 --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementTree.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.metrics.publish; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.Meter.Id; +import io.micrometer.core.instrument.Statistic; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.distribution.CountAtBucket; +import io.micrometer.core.instrument.distribution.HistogramSnapshot; + +// like select * from meters group by ...... +// but output a tree not a table +public class MeasurementTree extends MeasurementNode { + public MeasurementTree() { + super(null, null, null); + } + + // groupConfig: + // key: id name + // value: id tag keys + // only id name exists in groupConfig will accept, others will be ignored + public void from(Iterator meters, MeasurementGroupConfig groupConfig) { + meters.forEachRemaining(meter -> { + // This code snip is not very good design. But DistributionSummary is quite special. + if (meter instanceof DistributionSummary distributionSummary) { + HistogramSnapshot snapshot = distributionSummary.takeSnapshot(); + CountAtBucket[] countAtBuckets = snapshot.histogramCounts(); + List distributions = new ArrayList<>(countAtBuckets.length); + for (CountAtBucket countAtBucket : countAtBuckets) { + final double value = countAtBucket.count(); + distributions.add(new Measurement(() -> value, + Statistic.COUNT)); + } + + from(meter.getId(), distributions, groupConfig); + return; + } + + Iterable measurements = meter.measure(); + from(meter.getId(), measurements, groupConfig); + }); + } + + public void from(Id id, Iterable measurements, MeasurementGroupConfig groupConfig) { + for (Measurement measurement : measurements) { + MeasurementNode node = addChild(id.getName(), id, measurement); + + List tagFinders = groupConfig.findTagFinders(id.getName()); + if (tagFinders == null) { + continue; + } + + for (TagFinder tagFinder : tagFinders) { + Tag tag = tagFinder.find(id.getTags()); + if (tag == null) { + if (tagFinder.skipOnNull()) { + break; + } + throw new IllegalStateException( + String.format("tag key \"%s\" not exist in %s", + tagFinder.getTagKey(), + id)); + } + + node = node.addChild(tag.getValue(), id, measurement); + } + + node.addChild(measurement.getStatistic().name(), id, measurement); + } + } +} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TagFinder.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/TagFinder.java similarity index 92% rename from foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TagFinder.java rename to foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/TagFinder.java index 0762afb8de4..e1f3c8b402c 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TagFinder.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/TagFinder.java @@ -14,9 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.foundation.metrics.publish.spectator; +package org.apache.servicecomb.foundation.metrics.publish; -import com.netflix.spectator.api.Tag; +import io.micrometer.core.instrument.Tag; public interface TagFinder { static TagFinder build(Object obj) { diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementTree.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementTree.java deleted file mode 100644 index 79101fa166e..00000000000 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementTree.java +++ /dev/null @@ -1,71 +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.servicecomb.foundation.metrics.publish.spectator; - -import java.util.Iterator; -import java.util.List; - -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Meter; -import com.netflix.spectator.api.Tag; - -// like select * from meters group by ...... -// but output a tree not a table -public class MeasurementTree extends MeasurementNode { - public MeasurementTree() { - super(null, null); - } - - // groupConfig: - // key: id name - // value: id tag keys - // only id name exists in groupConfig will accept, others will be ignored - public void from(Iterator meters, MeasurementGroupConfig groupConfig) { - meters.forEachRemaining(meter -> { - Iterable measurements = meter.measure(); - from(measurements, groupConfig); - }); - } - - public void from(Iterable measurements, MeasurementGroupConfig groupConfig) { - for (Measurement measurement : measurements) { - Id id = measurement.id(); - MeasurementNode node = addChild(id.name(), measurement); - - List tagFinders = groupConfig.findTagFinders(id.name()); - if (tagFinders == null) { - continue; - } - - for (TagFinder tagFinder : tagFinders) { - Tag tag = tagFinder.find(id.tags()); - if (tag == null) { - if (tagFinder.skipOnNull()) { - break; - } - throw new IllegalStateException( - String.format("tag key \"%s\" not exist in %s", - tagFinder.getTagKey(), - measurement)); - } - - node = node.addChild(tag.value(), measurement); - } - } - } -} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/registry/GlobalRegistry.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/registry/GlobalRegistry.java deleted file mode 100644 index 25e85b134e5..00000000000 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/registry/GlobalRegistry.java +++ /dev/null @@ -1,114 +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.servicecomb.foundation.metrics.registry; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.apache.servicecomb.foundation.metrics.PolledEvent; -import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; - -import com.netflix.spectator.api.Clock; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Meter; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.SpectatorUtils; - -public class GlobalRegistry { - private final Clock clock; - - private final List registries = new CopyOnWriteArrayList<>(); - - private Registry defaultRegistry; - - public GlobalRegistry() { - this(Clock.SYSTEM); - } - - public GlobalRegistry(Clock clock) { - this.clock = clock; - } - - public Clock getClock() { - return clock; - } - - public List getRegistries() { - return registries; - } - - public Registry getDefaultRegistry() { - return defaultRegistry; - } - - public synchronized void add(Registry registry) { - if (registries.isEmpty()) { - defaultRegistry = registry; - } - registries.add(registry); - } - - public synchronized void remove(Registry registry) { - registries.remove(registry); - if (registry != defaultRegistry) { - return; - } - - if (registries.isEmpty()) { - defaultRegistry = null; - return; - } - - defaultRegistry = registries.get(0); - } - - public synchronized void removeAll() { - registries.clear(); - defaultRegistry = null; - } - - @SuppressWarnings("unchecked") - public T find(Class cls) { - for (Registry registry : registries) { - if (cls.isAssignableFrom(registry.getClass())) { - return (T) registry; - } - } - return null; - } - - public PolledEvent poll(long secondInterval) { - long msNow = clock.wallTime(); - List meters = new ArrayList<>(); - List measurements = new ArrayList<>(); - for (Registry registry : registries) { - SpectatorUtils.removeExpiredMeters(registry); - - for (Meter meter : registry) { - if (meter instanceof PeriodMeter) { - ((PeriodMeter) meter).calcMeasurements(msNow, secondInterval); - } - - meters.add(meter); - meter.measure().forEach(measurements::add); - } - } - - return new PolledEvent(meters, measurements); - } -} diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java index 8e93f0bef9f..231b0099ed4 100644 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java @@ -16,93 +16,69 @@ */ package org.apache.servicecomb.foundation.metrics; +import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN; +import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.DEFAULT_METRICS_WINDOW_TIME; +import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.METRICS_WINDOW_TIME; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Meter; -import com.netflix.spectator.api.Registry; +import io.micrometer.core.instrument.MeterRegistry; import mockit.Deencapsulation; -import mockit.Expectations; -import mockit.Mocked; -import org.junit.jupiter.api.Assertions; public class TestMetricsBootstrap { - MetricsBootstrap bootstrap = new MetricsBootstrap(); - - GlobalRegistry globalRegistry = new GlobalRegistry(); + MetricsBootstrap bootstrap; EventBus eventBus = new EventBus(); + Environment environment = Mockito.mock(Environment.class); + + @BeforeEach + public void setUp() { + Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) + .thenReturn(DEFAULT_METRICS_WINDOW_TIME); + Mockito.when(environment.getProperty( + CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) + .thenReturn(7); + + MetricsBootstrapConfig config = new MetricsBootstrapConfig(environment); + bootstrap = new MetricsBootstrap(config); + bootstrap.setMetricsInitializers(List.of()); + } + @Test public void loadMetricsInitializers() { List initList = new ArrayList<>(); MetricsInitializer metricsInitializer = new MetricsInitializer() { @Override - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { initList.add(this); } }; - new Expectations(SPIServiceUtils.class) { - { - SPIServiceUtils.getSortedService(MetricsInitializer.class); - result = Arrays.asList(metricsInitializer, metricsInitializer); - } - }; - bootstrap.start(globalRegistry, eventBus); + bootstrap.setMetricsInitializers(Arrays.asList(metricsInitializer, metricsInitializer)); + + bootstrap.start(eventBus); bootstrap.shutdown(); MatcherAssert.assertThat(initList, Matchers.contains(metricsInitializer, metricsInitializer)); } @Test - public void pollMeters(@Mocked Registry registry, @Mocked Meter meter, @Mocked Measurement measurement, - @Mocked ScheduledExecutorService executor) { - List meters = Arrays.asList(meter); - globalRegistry.add(registry); - new Expectations(Executors.class) { - { - Executors.newScheduledThreadPool(1, (ThreadFactory) any); - result = executor; - registry.iterator(); - result = meters.iterator(); - meter.measure(); - result = Arrays.asList(measurement); - } - }; - bootstrap.start(globalRegistry, eventBus); - - PolledEvent result = new PolledEvent(null, null); - eventBus.register(new Object() { - @Subscribe - public void onEvent(PolledEvent event) { - result.setMeters(event.getMeters()); - result.setMeasurements(event.getMeasurements()); - } - }); - - bootstrap.pollMeters(); - bootstrap.shutdown(); - Assertions.assertEquals(meters, result.getMeters()); - MatcherAssert.assertThat(result.getMeasurements(), Matchers.contains(measurement)); - } - - @Test - public void shutdown(@Mocked ScheduledExecutorService scheduledExecutorService) { + public void shutdown() { + ScheduledExecutorService scheduledExecutorService = Mockito.mock(ScheduledExecutorService.class); List destroyList = new ArrayList<>(); MetricsInitializer initializer1 = new MetricsInitializer() { @Override @@ -111,7 +87,7 @@ public int getOrder() { } @Override - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { } @Override @@ -127,7 +103,7 @@ public int getOrder() { } @Override - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { } @Override @@ -135,13 +111,7 @@ public void destroy() { destroyList.add(this); } }; - - new Expectations(SPIServiceUtils.class) { - { - SPIServiceUtils.getSortedService(MetricsInitializer.class); - result = Arrays.asList(initializer1, initializer2); - } - }; + bootstrap.setMetricsInitializers(Arrays.asList(initializer1, initializer2)); Deencapsulation.setField(bootstrap, "executorService", scheduledExecutorService); bootstrap.shutdown(); diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestLatencyDistributionMeter.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestLatencyDistributionMeter.java deleted file mode 100644 index d5cd260749c..00000000000 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestLatencyDistributionMeter.java +++ /dev/null @@ -1,47 +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.servicecomb.foundation.metrics.meter; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -public class TestLatencyDistributionMeter { - - @Test - public void testMeasure() { - Id id = Mockito.mock(Id.class); - LatencyDistributionMeter latencyDistributionMeter = new LatencyDistributionMeter(id, "0,1,3,10"); - latencyDistributionMeter.record(TimeUnit.MILLISECONDS.toNanos(1L)); - latencyDistributionMeter.record(TimeUnit.MILLISECONDS.toNanos(5L)); - latencyDistributionMeter.record(TimeUnit.MILLISECONDS.toNanos(2L)); - List measurements = new ArrayList<>(); - latencyDistributionMeter.calcMeasurements(measurements, 0L, 0L); - Assertions.assertEquals(4, measurements.size()); - Assertions.assertEquals(0, ((int) (measurements.get(0).value()))); - Assertions.assertEquals(2, ((int) (measurements.get(1).value()))); - Assertions.assertEquals(1, ((int) (measurements.get(2).value()))); - Assertions.assertEquals(0, ((int) (measurements.get(3).value()))); - } -} diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestSimpleTimer.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestSimpleTimer.java deleted file mode 100644 index 47b60ec4748..00000000000 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestSimpleTimer.java +++ /dev/null @@ -1,40 +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.servicecomb.foundation.metrics.meter; - -import com.netflix.spectator.api.SpectatorUtils; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestSimpleTimer { - SimpleTimer timer = new SimpleTimer(SpectatorUtils.createDefaultId("name")); - - @Test - public void measure() { - timer.record(2); - timer.record(4); - - Assertions.assertFalse(timer.measure().iterator().hasNext()); - - timer.calcMeasurements(1, 2); - Assertions.assertEquals( - "[Measurement(name:statistic=count,1,1.0), Measurement(name:statistic=totalTime,1,3.0000000000000004E-9), Measurement(name:statistic=max,1,4.0E-9)]", - timer.measure().toString()); - Assertions.assertFalse(timer.hasExpired()); - Assertions.assertEquals("name", timer.id().name()); - } -} diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestDefaultTagFinder.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestDefaultTagFinder.java index c892b6a8b8c..46220069b97 100644 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestDefaultTagFinder.java +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestDefaultTagFinder.java @@ -19,11 +19,13 @@ import java.util.Arrays; import java.util.List; -import com.netflix.spectator.api.BasicTag; -import com.netflix.spectator.api.Tag; +import org.apache.servicecomb.foundation.metrics.publish.DefaultTagFinder; +import org.apache.servicecomb.foundation.metrics.publish.TagFinder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import io.micrometer.core.instrument.Tag; + public class TestDefaultTagFinder { TagFinder finder = new DefaultTagFinder("key"); @@ -34,8 +36,8 @@ public void getTagKey() { @Test public void readSucc() { - Tag tag = new BasicTag("key", "value"); - List tags = Arrays.asList(new BasicTag("t1", "t1v"), + Tag tag = Tag.of("key", "value"); + List tags = Arrays.asList(Tag.of("t1", "t1v"), tag); Assertions.assertSame(tag, finder.find(tags)); @@ -43,7 +45,7 @@ public void readSucc() { @Test public void readFail() { - List tags = Arrays.asList(new BasicTag("t1", "t1v")); + List tags = Arrays.asList(Tag.of("t1", "t1v")); Assertions.assertNull(finder.find(tags)); } diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementGroupConfig.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementGroupConfig.java index 03374009621..60d77b4c75a 100644 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementGroupConfig.java +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementGroupConfig.java @@ -19,6 +19,8 @@ import java.util.List; import java.util.Map; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; +import org.apache.servicecomb.foundation.metrics.publish.TagFinder; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; @@ -51,7 +53,7 @@ public void addGroup() { config.addGroup("id1", "tag1.1", "tag1.2"); config.addGroup("id2", "tag2.1", "tag2.2"); - MatcherAssert.assertThat(groups.keySet(), Matchers.contains("id2", "id1")); + MatcherAssert.assertThat(groups.keySet(), Matchers.containsInAnyOrder("id2", "id1")); MatcherAssert.assertThat(groups.get("id1").stream().map(TagFinder::getTagKey).toArray(), Matchers.arrayContaining("tag1.1", "tag1.2")); MatcherAssert.assertThat(groups.get("id2").stream().map(TagFinder::getTagKey).toArray(), Matchers.arrayContaining("tag2.1", "tag2.2")); } diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java index 7c5691f929a..be7681ae5cd 100644 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java @@ -19,18 +19,23 @@ import java.util.HashMap; import java.util.Map; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; +import org.junit.jupiter.api.Assertions; -import com.netflix.spectator.api.Measurement; - +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter.Id; +import io.micrometer.core.instrument.Meter.Type; +import io.micrometer.core.instrument.Tags; import mockit.Expectations; import mockit.Mocked; -import org.junit.jupiter.api.Assertions; public class TestMeasurementNode { - MeasurementNode node = new MeasurementNode("name", null); + Id id = new Id("name", Tags.empty(), null, null, Type.OTHER); + + MeasurementNode node = new MeasurementNode("name", id, null); @Test public void getName() { @@ -40,7 +45,7 @@ public void getName() { @Test public void getChildren() { Map children = new HashMap<>(); - node = new MeasurementNode("name", children); + node = new MeasurementNode("name", id, children); Assertions.assertSame(children, node.getChildren()); } @@ -52,24 +57,24 @@ public void findChild_noChildren() { @Test public void findChild_multiLevel_noMiddleChildren(@Mocked Measurement measurement) { - MeasurementNode c1 = node.addChild("c1", measurement); - c1.addChild("c2", measurement); + MeasurementNode c1 = node.addChild("c1", id, measurement); + c1.addChild("c2", id, measurement); Assertions.assertNull(node.findChild("c1_notExist", "c2")); } @Test public void findChild_multiLevel_ok(@Mocked Measurement measurement) { - MeasurementNode c1 = node.addChild("c1", measurement); - MeasurementNode c2 = c1.addChild("c2", measurement); + MeasurementNode c1 = node.addChild("c1", id, measurement); + MeasurementNode c2 = c1.addChild("c2", id, measurement); Assertions.assertSame(c2, node.findChild("c1", "c2")); } @Test public void addChild(@Mocked Measurement measurement) { - MeasurementNode c1 = node.addChild("c1", measurement); - MeasurementNode c2 = node.addChild("c2", measurement); + MeasurementNode c1 = node.addChild("c1", id, measurement); + MeasurementNode c2 = node.addChild("c2", id, measurement); Assertions.assertSame(c1, node.findChild("c1")); Assertions.assertSame(c2, node.findChild("c2")); @@ -86,7 +91,7 @@ public void getMeasurements(@Mocked Measurement measurement) { public void summary(@Mocked Measurement measurement) { new Expectations() { { - measurement.value(); + measurement.getValue(); result = 10; } }; diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementTree.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementTree.java index 108831d0b70..d21354be5b2 100644 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementTree.java +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementTree.java @@ -18,22 +18,23 @@ import java.util.concurrent.TimeUnit; +import org.apache.servicecomb.foundation.metrics.publish.DefaultTagFinder; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; -import com.netflix.spectator.api.DefaultRegistry; -import com.netflix.spectator.api.ManualClock; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.Statistic; -import com.netflix.spectator.api.Timer; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Statistic; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; public class TestMeasurementTree { MeasurementTree tree = new MeasurementTree(); - ManualClock clock = new ManualClock(); - - Registry registry = new DefaultRegistry(clock); + MeterRegistry registry = new SimpleMeterRegistry(); Timer timer; @@ -53,17 +54,19 @@ public void setup() { @Test public void from() { - timer.record(10, TimeUnit.NANOSECONDS); - timer.record(2, TimeUnit.NANOSECONDS); + timer.record(10, TimeUnit.SECONDS); + timer.record(2, TimeUnit.SECONDS); - MeasurementGroupConfig config = new MeasurementGroupConfig("id", "g1", "g2", Statistic.count.key()); - tree.from(registry.iterator(), config); + MeasurementGroupConfig config = new MeasurementGroupConfig("id", "g1", "g2"); + tree.from(registry.getMeters().iterator(), config); Assertions.assertEquals(2, tree.getChildren().size()); MeasurementNode node = tree.findChild("id", "g1v", "g2v"); - Assertions.assertEquals(2d, node.findChild(Statistic.count.value()).getMeasurements().get(0).value(), 0); - Assertions.assertEquals(12d, node.findChild(Statistic.totalTime.value()).getMeasurements().get(0).value(), 0); + Assertions.assertEquals(2d, + node.findChild(Statistic.COUNT.name()).getMeasurements().get(0).getValue(), 0); + Assertions.assertEquals(12d, + node.findChild(Statistic.TOTAL_TIME.name()).getMeasurements().get(0).getValue(), 0); Assertions.assertEquals(0d, tree.findChild("id_notCare").summary(), 0); } @@ -71,7 +74,7 @@ public void from() { public void from_withSkipOnNull() { try { MeasurementGroupConfig config = new MeasurementGroupConfig("id", new DefaultTagFinder("notExist", true)); - tree.from(registry.iterator(), config); + tree.from(registry.getMeters().iterator(), config); } catch (Exception e) { Assertions.fail("should not throw exception"); } @@ -81,8 +84,10 @@ public void from_withSkipOnNull() { public void from_failed() { IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { MeasurementGroupConfig config = new MeasurementGroupConfig("id", "notExist"); - tree.from(registry.iterator(), config); + tree.from(registry.getMeters().iterator(), config); }); - Assertions.assertEquals("tag key \"notExist\" not exist in Measurement(id:g1=g1v:g2=g2v:statistic=count:t3=t3v:t4=t4v,0,0.0)", exception.getMessage()); + Assertions.assertEquals( + "tag key \"notExist\" not exist in MeterId{name='id', tags=[tag(g1=g1v),tag(g2=g2v),tag(t3=t3v),tag(t4=t4v)]}", + exception.getMessage()); } } diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestTagFinder.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestTagFinder.java index a4c40320e6a..2a0d960637a 100644 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestTagFinder.java +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestTagFinder.java @@ -16,6 +16,8 @@ */ package org.apache.servicecomb.foundation.metrics.publish.spectator; +import org.apache.servicecomb.foundation.metrics.publish.DefaultTagFinder; +import org.apache.servicecomb.foundation.metrics.publish.TagFinder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/foundations/foundation-protobuf/README.md b/foundations/foundation-protobuf/README.md index 8f3526373c7..c225e89dc87 100644 --- a/foundations/foundation-protobuf/README.md +++ b/foundations/foundation-protobuf/README.md @@ -1,9 +1,9 @@ # Overview -There are many existing protobuf codecs, but all of them are not suit for us. -ServiceComb data model is just POJO, not bind to any codec mechanism, so we had to create new one: +There are many existing protobuf codecs, but all of them are not suit for us. +ServiceComb data model is just POJO, not bind to any codec mechanism, so we had to create new one: -| | ServiceComb | protobuf | protostuff | jackson | -| -------------------------------------------------------- | :-----------: | :------: | :------------: | :-----: | +| | ServiceComb | protobuf | protostuff | jackson | +| -------------------------------------------------------- | :-----------: | :------: | :------------: | :-----: | | generate code | no need
just POJO | must
100+ lines IDL, generate 10000+ java code | no need
POJO with Annotation(eg:@Tag) | no need | | support null element in repeated field | no | no | not compatible | no | | support "oneOf" | no | yes | no | no | @@ -21,25 +21,25 @@ ServiceComb data model is just POJO, not bind to any codec mechanism, so we had | serialize not repeated number field ignore default value | yes | yes | no | no | | serialize from map model | yes | no | no | no | | deserialize to map model | yes | no | no | no | - + # Usage -## Create factory - one factory instance globally is enough +## Create factory + one factory instance globally is enough ```java ProtoMapperFactory factory = new ProtoMapperFactory(); ``` ## Load proto definition - create mapper instance for each proto definition -- load from classpath + create mapper instance for each proto definition +- load from classpath ```java ProtoMapper protoMapper = factory.createFromName("protobuf.proto"); ``` -- load from proto content +- load from proto content ```java ProtoMapper protoMapper = factory.createFromContent(protoContent); ``` ## Serialize -serializer is reusable and thread safe +serializer is reusable and thread safe Assuming you have a proto definition ```proto message User { @@ -50,10 +50,9 @@ and a POJO class ```java public class User { private String name; - // getter and setter + // getter and setter } ``` -```java RootSerializer serializer = protoMapper.createRootSerializer("User", User.class); User user = new User(); @@ -63,11 +62,13 @@ byte[] pojoBytes= serializer.serialize(user); Map map = new HashMap<>(); map.put("name", "userName"); byte[] mapBytes = serializer.serialize(map); +```java + // pojoBytes equals mapBytes ``` ## Deserialize -deserializer is reusable and thread safe +deserializer is reusable and thread safe ```java RootDeserializer pojoDeserializer = protoMapper.createRootDeserializer("User", User.class); RootDeserializer> mapDeserializer = protoMapper.createRootDeserializer("User", Map.class); @@ -86,67 +87,67 @@ Map map = mapDeserializer.deserialize(bytes); 2.protoStuff some scenes, there is no field but have getter or setter, so we can not use unsafe to access field so we disable protoStuff unsafe feature - + for repeated fields, protoStuff have better performance, but not compatible to protobuf - + 3.jackson not support map/any/recursive, ignore related fields 4.serialize result size ScbStrong/ScbWeak/Protobuf have the same and smaller size, because skip all default/null value -Empty: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 519 515 240 288 1242 -ser len : 36 0 0 0 56 -deser time(ms): 161 69 10 516 486 -deser->ser len: 36 0 0 0 56 -ser+deser(ms) : 680 584 250 804 1728 - -Scalars: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 557 529 328 262 1357 -ser len : 56 24 24 24 76 -deser time(ms): 181 141 115 527 504 -deser->ser len: 56 24 24 24 76 -ser+deser(ms) : 738 670 443 789 1861 - -Pojo: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 571 574 276 309 1304 -ser len : 46 10 10 10 66 -deser time(ms): 230 69 112 668 537 -deser->ser len: 46 10 10 10 66 -ser+deser(ms) : 801 643 388 977 1841 - -SimpleList: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 590 609 296 637 1320 -ser len : 68 32 32 32 88 -deser time(ms): 233 105 122 2226 541 -deser->ser len: 68 32 32 32 88 -ser+deser(ms) : 823 714 418 2863 1861 - -PojoList: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 609 632 319 2777 1407 -ser len : 56 20 20 20 76 -deser time(ms): 244 134 173 2287 679 -deser->ser len: 56 20 20 20 76 -ser+deser(ms) : 853 766 492 5064 2086 - -Map: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 746 772 491 1079 1298 -ser len : 92 54 54 54 56 -deser time(ms): 522 427 468 1031 422 -deser->ser len: 92 54 54 54 56 -ser+deser(ms) : 1268 1199 959 2110 1720 - -Mixed: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 1686 1999 2034 2112 2537 -ser len : 479 505 505 505 489 -deser time(ms): 1969 2154 2923 2984 3316 -deser->ser len: 479 505 505 505 489 -ser+deser(ms) : 3655 4153 4957 5096 5853 -``` \ No newline at end of file +Empty: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 519 515 240 288 1242 +serialize len : 36 0 0 0 56 +deserialize time(ms): 161 69 10 516 486 +deserialize->serialize len: 36 0 0 0 56 +serialize+deserialize(ms) : 680 584 250 804 1728 + +Scalars: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 557 529 328 262 1357 +serialize len : 56 24 24 24 76 +deserialize time(ms): 181 141 115 527 504 +deserialize->serialize len: 56 24 24 24 76 +serialize+deserialize(ms) : 738 670 443 789 1861 + +Pojo: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 571 574 276 309 1304 +serialize len : 46 10 10 10 66 +deserialize time(ms): 230 69 112 668 537 +deserialize->serialize len: 46 10 10 10 66 +serialize+deserialize(ms) : 801 643 388 977 1841 + +SimpleList: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 590 609 296 637 1320 +serialize len : 68 32 32 32 88 +deserialize time(ms): 233 105 122 2226 541 +deserialize->serialize len: 68 32 32 32 88 +serialize+deserialize(ms) : 823 714 418 2863 1861 + +PojoList: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 609 632 319 2777 1407 +serialize len : 56 20 20 20 76 +deserialize time(ms): 244 134 173 2287 679 +deserialize->serialize len: 56 20 20 20 76 +serialize+deserialize(ms) : 853 766 492 5064 2086 + +Map: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 746 772 491 1079 1298 +serialize len : 92 54 54 54 56 +deserialize time(ms): 522 427 468 1031 422 +deserialize->serialize len: 92 54 54 54 56 +serialize+deserialize(ms) : 1268 1199 959 2110 1720 + +Mixed: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 1686 1999 2034 2112 2537 +serialize len : 479 505 505 505 489 +deserialize time(ms): 1969 2154 2923 2984 3316 +deserialize->serialize len: 479 505 505 505 489 +serialize+deserialize(ms) : 3655 4153 4957 5096 5853 +``` diff --git a/foundations/foundation-protobuf/pom.xml b/foundations/foundation-protobuf/pom.xml index d542e63514b..cb8662c7bfb 100644 --- a/foundations/foundation-protobuf/pom.xml +++ b/foundations/foundation-protobuf/pom.xml @@ -21,7 +21,7 @@ foundations org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -50,10 +50,11 @@ jsr305
- + com.google.protobuf protobuf-java + test @@ -88,7 +89,7 @@ - generate-sources + generate-test-sources test-compile @@ -97,5 +98,5 @@ - + diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ContentFileReader.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ContentFileReader.java index 632de7bc054..89bf2b43282 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ContentFileReader.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ContentFileReader.java @@ -16,8 +16,6 @@ */ package org.apache.servicecomb.foundation.protobuf.internal.parser; -import javax.annotation.Nullable; - import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; @@ -25,20 +23,20 @@ public class ContentFileReader implements FileReader { private final FileReader importReader; + private final String content; - private boolean contentReaded; + private boolean contentRead; public ContentFileReader(FileReader importReader, String content) { this.importReader = importReader; this.content = content; } - @Nullable @Override public CharStream read(String contentOrName) { - if (!contentReaded) { - contentReaded = true; + if (!contentRead) { + contentRead = true; return CharStreams.fromString(this.content); } diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java index 3beaa0074fb..5d583954ca0 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java @@ -55,15 +55,14 @@ public abstract class SchemaManager { // key is canonical message name + ":" + canonical type name protected final Map> canonicalSchemas = new ConcurrentHashMapEx<>(); + // key is canonical message name + ":" + canonical type name + protected final Map> canonicalSchemasCreated = new ConcurrentHashMapEx<>(); + public SchemaManager(ProtoMapper protoMapper) { this.protoMapper = protoMapper; this.proto = protoMapper.getProto(); } - public Map> getCanonicalSchemas() { - return canonicalSchemas; - } - protected String generateCacheKey(Message message, JavaType javaType) { return message.getCanonicalName() + ":" + javaType.toCanonical(); } @@ -72,44 +71,25 @@ protected String generateCacheKey(Message message, JavaType javaType) { protected abstract SchemaEx newMessageSchema(Message message, Map types); - /** - * - * @param protoField - * @param propertyDescriptor provide getter/setter/javaType - * @return - */ protected abstract FieldSchema createScalarField(Field protoField, PropertyDescriptor propertyDescriptor); @SuppressWarnings("unchecked") protected SchemaEx getOrCreateMessageSchema(Message message, Map types) { String cacheKey = generateCacheKey(message, ProtoConst.MAP_TYPE); - SchemaEx messageSchema = (SchemaEx) canonicalSchemas.get(cacheKey); - if (messageSchema == null) { - // messageSchema already put into canonicalSchemas inside createMessageSchema - messageSchema = createMessageSchema(message, types); - } - return messageSchema; + return (SchemaEx) canonicalSchemasCreated.computeIfAbsent(cacheKey, key -> createMessageSchema(message, types)); } @SuppressWarnings("unchecked") protected SchemaEx getOrCreateMessageSchema(Message message, JavaType javaType) { String cacheKey = generateCacheKey(message, javaType); - SchemaEx messageSchema = (SchemaEx) canonicalSchemas.get(cacheKey); - if (messageSchema == null) { - // messageSchema already put into canonicalSchemas inside createMessageSchema - messageSchema = createMessageSchema(message, javaType); - } - return messageSchema; + return (SchemaEx) canonicalSchemasCreated.computeIfAbsent(cacheKey, + key -> createMessageSchema(message, javaType)); } @SuppressWarnings("unchecked") - protected SchemaEx findSchema(String key) { - return (SchemaEx) canonicalSchemas.get(key); - } - protected SchemaEx createMessageSchema(Message message, Map types) { String cacheKey = generateCacheKey(message, ProtoConst.MAP_TYPE); - SchemaEx schema = findSchema(cacheKey); + SchemaEx schema = (SchemaEx) canonicalSchemas.get(cacheKey); if (schema != null) { return schema; } @@ -121,9 +101,10 @@ protected SchemaEx createMessageSchema(Message message, Map return schema; } + @SuppressWarnings("unchecked") protected SchemaEx createMessageSchema(Message message, JavaType javaType) { String cacheKey = generateCacheKey(message, javaType); - SchemaEx schema = findSchema(cacheKey); + SchemaEx schema = (SchemaEx) canonicalSchemas.get(cacheKey); if (schema != null) { return schema; } @@ -144,7 +125,7 @@ protected FieldSchema createMapFieldSchema(Field protoField, PropertyDesc JavaType entryType = TypeFactory.defaultInstance().constructParametricType(MapEntry.class, javaType.getKeyType(), javaType.getContentType()); - SchemaEx> entrySchema = getOrCreateMessageSchema((Message) protoField.getType(), + SchemaEx> entrySchema = createMessageSchema((Message) protoField.getType(), entryType); return new MapSchema<>(protoField, propertyDescriptor, entrySchema); } @@ -185,14 +166,14 @@ public FieldMapEx> createMapFields(Message message, Map types, String perameterName) { + private JavaType getParameterType(Map types, String parameterName) { - if (types.get(perameterName) != null) { - return TypeFactory.defaultInstance().constructType(types.get(perameterName)); + if (types.get(parameterName) != null) { + return TypeFactory.defaultInstance().constructType(types.get(parameterName)); } throw new IllegalArgumentException( - String.format("not found type info for parameter name [%s]", perameterName)); + String.format("not found type info for parameter name [%s]", parameterName)); } public FieldSchema createSchemaField(Field protoField, PropertyDescriptor propertyDescriptor) { @@ -215,7 +196,7 @@ public FieldSchema createSchemaField(Field protoField, PropertyDescriptor // message if (protoField.getType().isMessage()) { - SchemaEx messageSchema = getOrCreateMessageSchema((Message) protoField.getType(), + SchemaEx messageSchema = createMessageSchema((Message) protoField.getType(), propertyDescriptor.getJavaType()); if (isWrapProperty((Message) protoField.getType())) { return new PropertyWrapperAsFieldSchema<>(protoField, propertyDescriptor, messageSchema); diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java index a86d2aa4fdc..23b9d85a01d 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java @@ -249,7 +249,7 @@ protected FieldSchema createRepeatedSchema(Field protoField, PropertyDesc if (contentType == null) { contentType = ProtoConst.OBJECT_TYPE; } - SchemaEx contentSchema = getOrCreateMessageSchema((Message) protoField.getType(), contentType); + SchemaEx contentSchema = createMessageSchema((Message) protoField.getType(), contentType); if (isWrapProperty((Message) protoField.getType())) { return PropertyWrapperRepeatedReadSchemas.create(protoField, propertyDescriptor, contentSchema); } diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/RepeatedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/RepeatedReadSchemas.java index 771bafdbce4..e03a7efd579 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/RepeatedReadSchemas.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/RepeatedReadSchemas.java @@ -21,6 +21,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; +import java.util.Set; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; @@ -75,7 +77,11 @@ public CollectionRepeatedSchema(Field protoField, PropertyDescriptor propertyDes public final int mergeFrom(InputEx input, T message) throws IOException { Collection collection = getter.get(message); if (collection == null) { - collection = new ArrayList<>(); + if (Set.class.equals(javaType.getRawClass())) { + collection = new HashSet<>(); + } else { + collection = new ArrayList<>(); + } setter.set(message, collection); } diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SInt32ReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SInt32ReadSchemas.java index 36621024b4c..8252e2a96ca 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SInt32ReadSchemas.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SInt32ReadSchemas.java @@ -18,7 +18,9 @@ import java.io.IOException; +import org.apache.servicecomb.foundation.common.utils.bean.ByteSetter; import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; +import org.apache.servicecomb.foundation.common.utils.bean.ShortSetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.AbstractScalarReadSchemas.AbstractIntSchema; @@ -36,7 +38,18 @@ public static FieldSchema create(Field protoField, PropertyDescriptor pro return new SInt32PrimitiveSchema<>(protoField, propertyDescriptor); } - if (Integer.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { + if (short.class.equals(javaType.getRawClass())) { + return new ShortFieldSInt32PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (byte.class.equals(javaType.getRawClass())) { + return new ByteFieldSInt32PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Integer.class.equals(javaType.getRawClass()) + || Byte.class.equals(javaType.getRawClass()) + || Short.class.equals(javaType.getRawClass()) + || javaType.isJavaLangObject()) { return new SInt32Schema<>(protoField, propertyDescriptor); } @@ -52,7 +65,13 @@ public SInt32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readSInt32(); - setter.set(message, value); + if (Byte.class.equals(javaType.getRawClass())) { + setter.set(message, (byte) value); + } else if (Short.class.equals(javaType.getRawClass())) { + setter.set(message, (short) value); + } else { + setter.set(message, value); + } return input.readFieldNumber(); } } @@ -72,4 +91,36 @@ public int mergeFrom(InputEx input, T message) throws IOException { return input.readFieldNumber(); } } + + private static class ShortFieldSInt32PrimitiveSchema extends FieldSchema { + protected final ShortSetter setter; + + public ShortFieldSInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + int value = input.readSInt32(); + setter.set(message, (short) value); + return input.readFieldNumber(); + } + } + + private static class ByteFieldSInt32PrimitiveSchema extends FieldSchema { + protected final ByteSetter setter; + + public ByteFieldSInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + int value = input.readSInt32(); + setter.set(message, (byte) value); + return input.readFieldNumber(); + } + } } diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/StringReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/StringReadSchemas.java index 0edaa827bee..7b1f0a441b0 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/StringReadSchemas.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/StringReadSchemas.java @@ -17,6 +17,8 @@ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; import org.apache.servicecomb.foundation.common.utils.bean.CharSetter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; @@ -42,6 +44,14 @@ public static FieldSchema create(Field protoField, PropertyDescriptor pro return new StringSchema<>(protoField, propertyDescriptor); } + if (BigDecimal.class.equals(javaType.getRawClass())) { + return new BigDecimalSchema<>(protoField, propertyDescriptor); + } + + if (BigInteger.class.equals(javaType.getRawClass())) { + return new BigIntegerSchema<>(protoField, propertyDescriptor); + } + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } @@ -82,4 +92,36 @@ public int mergeFrom(InputEx input, T message) throws IOException { return input.readFieldNumber(); } } + + private static class BigDecimalSchema extends FieldSchema { + private final Setter setter; + + public BigDecimalSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + String value = input.readString(); + setter.set(message, new BigDecimal(value)); + return input.readFieldNumber(); + } + } + + private static class BigIntegerSchema extends FieldSchema { + private final Setter setter; + + public BigIntegerSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + String value = input.readString(); + setter.set(message, new BigInteger(value)); + return input.readFieldNumber(); + } + } } diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java index 49b2154cbbc..ccab4d81f82 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java @@ -101,6 +101,8 @@ public FieldMapEx getMainPojoFieldMaps() { @Override public void init() { + this.mapFieldMaps = protoMapper.getSerializerSchemaManager().createMapFields(message); + if (ProtoUtils.isWrapProperty(message)) { this.mainPojoFieldMaps = createPropertyWrapperFields(javaType); return; @@ -178,10 +180,6 @@ private void writeDynamicPojo(OutputEx output, Object dynamicValue) throws I } protected final void writeFromMap(OutputEx output, Map map) throws IOException { - if (mapFieldMaps == null) { - mapFieldMaps = protoMapper.getSerializerSchemaManager().createMapFields(message); - } - for (Entry entry : map.entrySet()) { if (entry.getValue() == null) { continue; diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java index 97ea2af2b1d..d63944e3423 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java @@ -226,7 +226,7 @@ protected FieldSchema createRepeatedSchema(Field protoField, PropertyDesc if (contentType == null) { contentType = ProtoConst.OBJECT_TYPE; } - SchemaEx contentSchema = getOrCreateMessageSchema((Message) protoField.getType(), contentType); + SchemaEx contentSchema = createMessageSchema((Message) protoField.getType(), contentType); if (isWrapProperty((Message) protoField.getType())) { return PropertyWrapperRepeatedWriteSchemas.create(protoField, propertyDescriptor, contentSchema); } diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/StringWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/StringWriteSchemas.java index 34c8647ca10..7df329b849b 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/StringWriteSchemas.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/StringWriteSchemas.java @@ -17,6 +17,8 @@ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; import org.apache.servicecomb.foundation.common.utils.bean.CharGetter; import org.apache.servicecomb.foundation.common.utils.bean.Getter; @@ -65,6 +67,11 @@ public final void writeTo(OutputEx output, Object value) throws IOException { return; } + if (value instanceof BigDecimal || value instanceof BigInteger) { + output.writeScalarString(tag, tagSize, value.toString()); + return; + } + ProtoUtils.throwNotSupportWrite(protoField, value); } } diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/notice.txt b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/notice.txt index 870ad1c79bb..0b6783a90c6 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/notice.txt +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/notice.txt @@ -15,4 +15,4 @@ 1.数组编码规则从protoStuff变成标准的protobuf 不确定: - 1.List/List/Map>/Map规则应该也有变化,是否有变化,从protoStuff变成标准protobuf \ No newline at end of file + 1.List/List/Map>/Map规则应该也有变化,是否有变化,从protoStuff变成标准protobuf diff --git a/foundations/foundation-protobuf/src/main/resources/google/protobuf/any.proto b/foundations/foundation-protobuf/src/main/resources/google/protobuf/any.proto new file mode 100644 index 00000000000..c89243178af --- /dev/null +++ b/foundations/foundation-protobuf/src/main/resources/google/protobuf/any.proto @@ -0,0 +1,161 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option go_package = "google.golang.org/protobuf/types/known/anypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "AnyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// // or ... +// if (any.isSameTypeAs(Foo.getDefaultInstance())) { +// foo = any.unpack(Foo.getDefaultInstance()); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := anypb.New(foo) +// if err != nil { +// ... +// } +// ... +// foo := &pb.Foo{} +// if err := any.UnmarshalTo(foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// JSON +// ==== +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +// +message Any { + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). + // + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: + // + // * If no scheme is provided, `https` is assumed. + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + // + string type_url = 1; + + // Must be a valid serialized protocol buffer of the above specified type. + bytes value = 2; +} diff --git a/foundations/foundation-protobuf/src/main/resources/google/protobuf/empty.proto b/foundations/foundation-protobuf/src/main/resources/google/protobuf/empty.proto new file mode 100644 index 00000000000..b87c89dcfce --- /dev/null +++ b/foundations/foundation-protobuf/src/main/resources/google/protobuf/empty.proto @@ -0,0 +1,51 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option go_package = "google.golang.org/protobuf/types/known/emptypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +message Empty {} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBoolSchema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBoolSchema.java index a82fc971c34..5862c723bfc 100644 --- a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBoolSchema.java +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBoolSchema.java @@ -36,7 +36,7 @@ public void testTrue() throws Throwable { // equalsIgnoreCase doTestFromString("true"); - doTestFromString("trUe"); + doTestFromString("true"); } @Test @@ -83,4 +83,3 @@ public void type_invalid() throws Throwable { exception.getMessage()); } } - diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestBase.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestBase.java index c8c810c6505..c73caf9a327 100644 --- a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestBase.java +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestBase.java @@ -79,25 +79,25 @@ private TestEngineResult runOneEngine(ProtubufCodecEngine engine, Object model, { long msStart = System.currentTimeMillis(); for (int idx = 0; idx < count; idx++) { - engineResult.serBytes = engine.serialize(model); + engineResult.serializeBytes = engine.serialize(model); } long msEnd = System.currentTimeMillis(); - engineResult.msSerTime = msEnd - msStart; + engineResult.msSerializeTime = msEnd - msStart; } // deserialize { long msStart = System.currentTimeMillis(); for (int idx = 0; idx < count; idx++) { - engineResult.deserResult = engine.deserialize(engineResult.serBytes); + engineResult.deserializeResult = engine.deserialize(engineResult.serializeBytes); } long msEnd = System.currentTimeMillis(); - engineResult.msDeserTime = msEnd - msStart; + engineResult.msDeserializeTime = msEnd - msStart; } - engineResult.deserResultBytes = engine.serialize(engineResult.deserResult); + engineResult.deserializeResultBytes = engine.serialize(engineResult.deserializeResult); return engineResult; } } diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestEngineResult.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestEngineResult.java index ffa08506df7..f02ee12092a 100644 --- a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestEngineResult.java +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestEngineResult.java @@ -20,14 +20,14 @@ public class TestEngineResult { public String engineName; // serialize - public long msSerTime; + public long msSerializeTime; - public byte[] serBytes; + public byte[] serializeBytes; // deserialize - public long msDeserTime; + public long msDeserializeTime; - public Object deserResult; + public Object deserializeResult; - public byte[] deserResultBytes; + public byte[] deserializeResultBytes; } diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestProtoPerformance.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestProtoPerformance.java index eea36041283..98e2f5b7855 100644 --- a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestProtoPerformance.java +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestProtoPerformance.java @@ -65,17 +65,17 @@ private static void printResult(TestResult result) { System.out.printf(" " + strFmt + "\n", result.engineResults.stream().map(r -> r.engineName).toArray()); - System.out.printf("ser time(ms) : " + numberFmt + "\n", - result.engineResults.stream().map(r -> r.msSerTime).toArray()); - System.out.printf("ser len : " + numberFmt + "\n", - result.engineResults.stream().map(r -> r.serBytes.length).toArray()); + System.out.printf("serialize time(ms) : " + numberFmt + "\n", + result.engineResults.stream().map(r -> r.msSerializeTime).toArray()); + System.out.printf("serialize len : " + numberFmt + "\n", + result.engineResults.stream().map(r -> r.serializeBytes.length).toArray()); - System.out.printf("deser time(ms): " + numberFmt + "\n", - result.engineResults.stream().map(r -> r.msDeserTime).toArray()); - System.out.printf("deser->ser len: " + numberFmt + "\n", - result.engineResults.stream().map(r -> r.deserResultBytes.length).toArray()); + System.out.printf("deserialize time(ms): " + numberFmt + "\n", + result.engineResults.stream().map(r -> r.msDeserializeTime).toArray()); + System.out.printf("deserialize->serialize len: " + numberFmt + "\n", + result.engineResults.stream().map(r -> r.deserializeResultBytes.length).toArray()); - System.out.printf("ser+deser(ms) : " + numberFmt + "\n\n", - result.engineResults.stream().map(r -> r.msSerTime + r.msDeserTime).toArray()); + System.out.printf("serialize+deserialize(ms) : " + numberFmt + "\n\n", + result.engineResults.stream().map(r -> r.msSerializeTime + r.msDeserializeTime).toArray()); } } diff --git a/foundations/foundation-protobuf/src/test/resources/jacksonRoot.proto b/foundations/foundation-protobuf/src/test/resources/jacksonRoot.proto index 1a0e33938bd..b1804922666 100644 --- a/foundations/foundation-protobuf/src/test/resources/jacksonRoot.proto +++ b/foundations/foundation-protobuf/src/test/resources/jacksonRoot.proto @@ -5,9 +5,9 @@ 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. @@ -95,4 +95,4 @@ enum Color { message User { optional string name = 1; -} \ No newline at end of file +} diff --git a/foundations/foundation-protobuf/src/test/resources/method.proto b/foundations/foundation-protobuf/src/test/resources/method.proto index 08a948ca03f..414b7643880 100644 --- a/foundations/foundation-protobuf/src/test/resources/method.proto +++ b/foundations/foundation-protobuf/src/test/resources/method.proto @@ -42,4 +42,4 @@ message Response { message User { string name = 1; repeated User friends = 2; -} \ No newline at end of file +} diff --git a/foundations/foundation-protobuf/src/test/resources/protobufRoot.proto b/foundations/foundation-protobuf/src/test/resources/protobufRoot.proto index 0972a3133fa..1e9f7f41076 100644 --- a/foundations/foundation-protobuf/src/test/resources/protobufRoot.proto +++ b/foundations/foundation-protobuf/src/test/resources/protobufRoot.proto @@ -5,9 +5,9 @@ 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. @@ -109,4 +109,4 @@ message User { string name = 1; Root typeRecursive = 2; -} \ No newline at end of file +} diff --git a/foundations/foundation-registry/pom.xml b/foundations/foundation-registry/pom.xml index e524790f355..d4cca8111c4 100644 --- a/foundations/foundation-registry/pom.xml +++ b/foundations/foundation-registry/pom.xml @@ -21,7 +21,7 @@ foundations org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/DiscoveryManager.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/DiscoveryManager.java index df8713c74c1..edcd3b3caef 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/DiscoveryManager.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/DiscoveryManager.java @@ -18,140 +18,320 @@ package org.apache.servicecomb.registry; import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedList; +import java.util.HashMap; import java.util.List; -import java.util.concurrent.CompletableFuture; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; -import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.foundation.common.utils.SPIEnabled; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.foundation.common.NamedThreadFactory; +import org.apache.servicecomb.foundation.common.cache.VersionedCache; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.utils.LambdaUtils; import org.apache.servicecomb.registry.api.Discovery; +import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.api.LifeCycle; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstances; -import org.apache.servicecomb.registry.cache.InstanceCacheManager; -import org.apache.servicecomb.registry.cache.InstanceCacheManagerNew; -import org.apache.servicecomb.registry.consumer.AppManager; -import org.apache.servicecomb.registry.consumer.MicroserviceVersions; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.discovery.InstancePing; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.HistoryStatus; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.IsolationStatus; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.PingStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; -import com.google.common.annotations.VisibleForTesting; +public class DiscoveryManager implements LifeCycle { + public interface InstanceChangeListener { + void onInstancesChanged(String registryName, String application, String serviceName, + List instances); + } -public class DiscoveryManager { - public static DiscoveryManager INSTANCE = new DiscoveryManager(); + private static final Logger LOGGER = LoggerFactory.getLogger(DiscoveryManager.class); - private final List discoveryList = new ArrayList<>(); + private final ScheduledExecutorService task; - private final AppManager appManager; + private final List> discoveryList; - private final InstanceCacheManager instanceCacheManager; + private final InstancePing ping; + private final HealthCheckTask healthCheckTask = new HealthCheckTask(); - private DiscoveryManager() { - appManager = new AppManager(); - instanceCacheManager = new InstanceCacheManagerNew(appManager); - SPIServiceUtils.getOrLoadSortedService(Discovery.class) - .stream() - .filter((SPIEnabled::enabled)) - .forEach(discoveryList::add); - } + // application:serviceName:instanceId + private final Map>> + allInstances = new ConcurrentHashMapEx<>(); - @VisibleForTesting - public static void renewInstance() { - DiscoveryManager.INSTANCE = new DiscoveryManager(); - } + // application:serviceName + private final Map> + versionedCache = new ConcurrentHashMapEx<>(); - public MicroserviceInstances findServiceInstances(String appId, String serviceName, - String versionRule) { - return findServiceInstances(appId, serviceName, versionRule, null); - } + private final Object cacheLock = new Object(); - public MicroserviceInstances findServiceInstances(String appId, String serviceName, - String versionRule, String revision) { - MicroserviceInstances result = new MicroserviceInstances(); - // default values not suitable for aggregate, reset. - result.setNeedRefresh(false); - result.setMicroserviceNotExist(true); - result.setRevision(revision); - discoveryList - .forEach(discovery -> { - MicroserviceInstances instances = discovery.findServiceInstances(appId, serviceName, versionRule); - result.mergeMicroserviceInstances(instances); - }); + private final List instanceChangeListeners = new ArrayList<>(); - return result; + public DiscoveryManager(List> discoveryList, + List pings) { + this.discoveryList = discoveryList; + for (Discovery discovery : this.discoveryList) { + discovery.setInstanceChangedListener(this::onInstancesChanged); + } + this.ping = pings.get(0); + task = Executors.newScheduledThreadPool(1, new NamedThreadFactory("discovery-manager-task")); } - public InstanceCacheManager getInstanceCacheManager() { - return this.instanceCacheManager; + public Discovery getPrimaryDiscovery() { + return this.discoveryList.get(0); } - public AppManager getAppManager() { - return this.appManager; + private void onInstancesChanged(String application, String serviceName, + List instances) { + onInstancesChanged(null, application, serviceName, instances); } - public MicroserviceInstance getMicroserviceInstance(String serviceId, String instanceId) { - for (Discovery discovery : discoveryList) { - MicroserviceInstance microserviceInstance = discovery.getMicroserviceInstance(serviceId, instanceId); - if (microserviceInstance != null) { - return microserviceInstance; + private void onInstancesChanged(String registryName, String application, String serviceName, + List instances) { + Map statefulInstances = allInstances.computeIfAbsent(application, key -> + new ConcurrentHashMapEx<>()).computeIfAbsent(serviceName, key -> new ConcurrentHashMapEx<>()); + + for (StatefulDiscoveryInstance statefulInstance : statefulInstances.values()) { + if (registryName == null || registryName.equals(statefulInstance.getRegistryName())) { + if (!instances.contains(statefulInstance)) { + statefulInstance.setPingTime(0); + statefulInstance.setHistoryStatus(HistoryStatus.HISTORY); + } } } - return null; - } - public String getSchema(String microserviceId, Collection instances, String schemaId) { - for (Discovery discovery : discoveryList) { - String schema = discovery.getSchema(microserviceId, instances, schemaId); - if (schema != null) { - return schema; + for (DiscoveryInstance instance : instances) { + StatefulDiscoveryInstance target = new StatefulDiscoveryInstance(instance); + StatefulDiscoveryInstance origin = statefulInstances.get(instance.getInstanceId()); + if (origin == null) { + statefulInstances.put(instance.getInstanceId(), target); + continue; } + target.setPingTime(origin.getPingTime()); + target.setPingStatus(origin.getPingStatus()); + target.setIsolateDuration(origin.getIsolateDuration()); + target.setIsolationStatus(origin.getIsolationStatus()); + statefulInstances.put(instance.getInstanceId(), target); + } + + StringBuilder instanceInfo = new StringBuilder(); + for (DiscoveryInstance instance : instances) { + instanceInfo.append("{") + .append(instance.getInstanceId()).append(",") + .append(instance.getStatus()).append(",") + .append(instance.getEndpoints()).append(",") + .append(instance.getRegistryName()) + .append("}"); + } + LOGGER.info("Applying new instance list for {}/{}/{}. Endpoints {}", + application, serviceName, instances.size(), instanceInfo); + + rebuildVersionCache(application, serviceName); + + for (InstanceChangeListener listener : this.instanceChangeListeners) { + listener.onInstancesChanged(registryName, application, serviceName, instances); } - return null; } - public Microservice getMicroservice(String microserviceId) { - for (Discovery discovery : discoveryList) { - Microservice microservice = discovery.getMicroservice(microserviceId); - if (microservice != null) { - return microservice; - } + public void addInstanceChangeListener(InstanceChangeListener instanceChangeListener) { + this.instanceChangeListeners.add(instanceChangeListener); + } + + public void onInstanceIsolated(DiscoveryInstance instance, long isolateDuration) { + Map statefulInstances = allInstances.computeIfAbsent( + instance.getApplication(), key -> + new ConcurrentHashMapEx<>()).computeIfAbsent(instance.getServiceName(), key + -> new ConcurrentHashMapEx<>()); + StatefulDiscoveryInstance target = statefulInstances.get(instance.getInstanceId()); + if (target == null) { + return; } - return null; + + target.setIsolatedTime(System.currentTimeMillis()); + target.setIsolateDuration(isolateDuration); + + if (target.getIsolationStatus() != IsolationStatus.ISOLATED) { + target.setIsolationStatus(IsolationStatus.ISOLATED); + rebuildVersionCache(instance.getApplication(), instance.getServiceName()); + } + + LOGGER.warn("Isolated instance {}/{}/{}, time {}/{}", + instance.getApplication(), instance.getServiceName(), instance.getInstanceId(), + target.getIsolatedTime(), target.getIsolateDuration()); } - public List getAllMicroservices() { - List result = new LinkedList<>(); - for (Discovery discovery : discoveryList) { - List microservices = discovery.getAllMicroservices(); - if (microservices != null) { - result.addAll(microservices); + private void rebuildVersionCache(String application, String serviceName) { + Map caches = versionedCache.computeIfAbsent(application, key -> + new ConcurrentHashMapEx<>()); + caches.put(serviceName, calcAvailableInstance(application, serviceName)); + } + + private VersionedCache calcAvailableInstance(String application, String serviceName) { + Map statefulInstances = allInstances.computeIfAbsent( + application, key -> + new ConcurrentHashMapEx<>()).computeIfAbsent(serviceName, key + -> new ConcurrentHashMapEx<>()); + List result = new ArrayList<>(); + for (StatefulDiscoveryInstance instance : statefulInstances.values()) { + if (instance.getHistoryStatus() == HistoryStatus.CURRENT) { + result.add(instance); + continue; + } + if (instance.getHistoryStatus() == HistoryStatus.HISTORY + && instance.getStatus() == MicroserviceInstanceStatus.UP + && instance.getPingStatus() == PingStatus.OK + && instance.getIsolationStatus() == IsolationStatus.NORMAL) { + result.add(instance); } } - return result; + StringBuilder instanceInfo = new StringBuilder(); + for (StatefulDiscoveryInstance instance : result) { + instanceInfo.append("{") + .append(instance.getInstanceId()).append(",") + .append(instance.getHistoryStatus()).append(",") + .append(instance.getStatus()).append(",") + .append(instance.getPingStatus()).append(",") + .append(instance.getIsolationStatus()).append(",") + .append(instance.getEndpoints()).append(",") + .append(instance.getRegistryName()) + .append("}"); + } + LOGGER.info("Rebuild cached instance list for {}/{}/{}. Endpoints {}", + application, serviceName, result.size(), instanceInfo); + return new VersionedCache() + .name(application + ":" + serviceName) + .autoCacheVersion() + .data(result); } - public CompletableFuture getOrCreateMicroserviceVersionsAsync(String appId, - String microserviceName) { - return appManager.getOrCreateMicroserviceVersionsAsync(appId, microserviceName); + public VersionedCache getOrCreateVersionedCache(String application, String serviceName) { + Map caches = versionedCache.computeIfAbsent(application, key -> + new ConcurrentHashMapEx<>()); + VersionedCache cache = caches.get(serviceName); + if (cache == null) { + synchronized (cacheLock) { + cache = caches.get(serviceName); + if (cache != null) { + return cache; + } + List instances = findServiceInstances(application, serviceName); + onInstancesChanged(application, serviceName, instances); + return versionedCache.get(application).get(serviceName); + } + } + return cache; } - public MicroserviceVersions getOrCreateMicroserviceVersions(String appId, String microserviceName) { - return appManager.getOrCreateMicroserviceVersions(appId, microserviceName); + public List findServiceInstances(String application, String serviceName) { + List result = new ArrayList<>(); + for (Discovery discovery : discoveryList) { + if (!discovery.enabled() || !discovery.enabled(application, serviceName)) { + continue; + } + List temp = discovery.findServiceInstances(application, serviceName); + if (CollectionUtils.isEmpty(temp)) { + continue; + } + result.addAll(temp); + } + return result; } + @Override public void destroy() { - discoveryList.forEach(LifeCycle::destroy); + discoveryList.forEach(LambdaUtils.ignoreException(LifeCycle::destroy)); + task.shutdownNow(); } + @Override public void run() { discoveryList.forEach(LifeCycle::run); + task.schedule(healthCheckTask, 3, TimeUnit.SECONDS); } + @Override public void init() { - BeanUtils.addBeans(Discovery.class, discoveryList); - discoveryList.forEach(LifeCycle::init); } + + public String info() { + StringBuilder result = new StringBuilder(); + AtomicBoolean first = new AtomicBoolean(true); + discoveryList.forEach(discovery -> { + if (first.getAndSet(false)) { + result.append("Discovery implementations:\n"); + } + result.append(" name:").append(discovery.name()).append("\n"); + }); + return result.toString(); + } + + class HealthCheckTask implements Runnable { + + @Override + public void run() { + try { + Map>> removed = new HashMap<>(); + for (Entry>> apps : DiscoveryManager.this.allInstances.entrySet()) { + for (Entry> services : apps.getValue().entrySet()) { + boolean changed = false; + for (StatefulDiscoveryInstance instance : services.getValue().values()) { + // check isolated time + if (instance.getIsolationStatus() == IsolationStatus.ISOLATED && + instance.getIsolatedTime() + instance.getIsolateDuration() < System.currentTimeMillis()) { + instance.setIsolationStatus(IsolationStatus.NORMAL); + changed = true; + } + // check ping status + if (System.currentTimeMillis() - instance.getPingTime() > 60_000L) { + boolean pingResult = DiscoveryManager.this.ping.ping(instance); + if (pingResult && instance.getPingStatus() != PingStatus.OK) { + instance.setPingStatus(PingStatus.OK); + changed = true; + } else if (!pingResult && instance.getPingStatus() != PingStatus.FAIL) { + instance.setPingStatus(PingStatus.FAIL); + changed = true; + } + instance.setPingTime(System.currentTimeMillis()); + } + // check unused + if (instance.getHistoryStatus() == HistoryStatus.HISTORY) { + if (instance.getStatus() != MicroserviceInstanceStatus.UP || + instance.getPingStatus() == PingStatus.FAIL || + instance.getIsolationStatus() == IsolationStatus.ISOLATED) { + removed.computeIfAbsent(apps.getKey(), k -> new HashMap<>()) + .computeIfAbsent(services.getKey(), k -> new ArrayList<>()).add(instance.getInstanceId()); + LOGGER.info("Remove instance {}/{}/{}/{}/{}/{}/{}/{}", + apps.getKey(), services.getKey(), instance.getRegistryName(), + instance.getInstanceId(), instance.getHistoryStatus(), + instance.getStatus(), instance.getPingStatus(), instance.getIsolationStatus()); + changed = true; + } + } + } + if (changed) { + rebuildVersionCache(apps.getKey(), services.getKey()); + } + } + } + // remove unused + for (Entry>> apps : removed.entrySet()) { + for (Entry> services : apps.getValue().entrySet()) { + for (String instance : services.getValue()) { + DiscoveryManager.this.allInstances.get(apps.getKey()).get(services.getKey()).remove(instance); + } + } + } + } catch (Throwable e) { + LOGGER.error("discovery manager task error. ", e); + } finally { + DiscoveryManager.this.task.schedule(this, 3, TimeUnit.SECONDS); + } + } + } } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationId.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationId.java new file mode 100644 index 00000000000..9932f5fdfc9 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationId.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.servicecomb.registry; + +import java.util.UUID; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RegistrationId { + private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationId.class); + + private final String instanceId; + + public RegistrationId() { + this.instanceId = buildInstanceId(); + LOGGER.info("initialized global registration id {}", this.instanceId); + } + + public String getInstanceId() { + return instanceId; + } + + private static String buildInstanceId() { + return UUID.randomUUID().toString(); + } +} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationManager.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationManager.java index be59114a5e7..9c135d380c8 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationManager.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationManager.java @@ -17,103 +17,77 @@ package org.apache.servicecomb.registry; -import java.net.Inet6Address; -import java.net.InetSocketAddress; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; -import com.google.common.annotations.VisibleForTesting; -import org.apache.http.client.utils.URIBuilder; -import org.apache.servicecomb.foundation.common.event.EnableExceptionPropagation; -import org.apache.servicecomb.foundation.common.event.EventManager; -import org.apache.servicecomb.foundation.common.net.IpPort; -import org.apache.servicecomb.foundation.common.net.NetUtils; -import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.apache.servicecomb.foundation.common.utils.LambdaUtils; import org.apache.servicecomb.foundation.common.utils.SPIEnabled; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.registry.api.LifeCycle; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.Registration; -import org.apache.servicecomb.registry.api.event.MicroserviceInstanceRegisteredEvent; -import org.apache.servicecomb.registry.api.registry.BasePath; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstanceStatus; -import org.apache.servicecomb.registry.consumer.MicroserviceManager; -import org.apache.servicecomb.registry.consumer.StaticMicroserviceVersions; -import org.apache.servicecomb.registry.definition.MicroserviceNameParser; -import org.apache.servicecomb.registry.swagger.SwaggerLoader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.eventbus.Subscribe; -import com.netflix.config.DynamicPropertyFactory; +import org.apache.servicecomb.registry.api.RegistrationInstance; +import org.springframework.util.CollectionUtils; import io.vertx.core.json.jackson.JacksonFactory; public class RegistrationManager { - private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationManager.class); - - // value is ip or {interface name} - public static final String PUBLISH_ADDRESS = "servicecomb.service.publishAddress"; - - private static final String PUBLISH_PORT = "servicecomb.{transport_name}.publishPort"; - - private static final SwaggerLoader swaggerLoader = new SwaggerLoader(); - - public static RegistrationManager INSTANCE = new RegistrationManager(); - - private final List registrationList = new ArrayList<>(); - - private Registration primary; - - private RegistrationManager() { - SPIServiceUtils.getOrLoadSortedService(Registration.class) - .stream() - .filter((SPIEnabled::enabled)) - .forEach(registrationList::add); - initPrimary(); - } - - @VisibleForTesting - public static void setINSTANCE(RegistrationManager INSTANCE) { - RegistrationManager.INSTANCE = INSTANCE; - } + private final List> registrationList; - public MicroserviceInstance getMicroserviceInstance() { - return primary.getMicroserviceInstance(); + public RegistrationManager(List> registrationList) { + if (registrationList == null) { + this.registrationList = Collections.emptyList(); + return; + } + this.registrationList = registrationList.stream().filter(SPIEnabled::enabled).collect(Collectors.toList()); } - public Microservice getMicroservice() { - assertPrimaryNotNull(); - return primary.getMicroservice(); + /** + * For internal use. Get instance id from registry name. If not exists, return empty. + */ + public String getInstanceId(String registryName) { + if (CollectionUtils.isEmpty(registrationList)) { + return ""; + } + Optional> registration = + registrationList.stream().filter(r -> registryName.equals(r.name())).collect(Collectors.toList()) + .stream().findFirst(); + if (!registration.isPresent()) { + return ""; + } + return registration.get().getMicroserviceInstance().getInstanceId(); } - private void assertPrimaryNotNull() { - if (primary == null) { - throw new NullPointerException("At least one Registration implementation configured. Missed" - + " to include dependency ? e.g. registry-service-center"); + /** + * For internal use. Get service id from registry name. If not exists, return empty. + */ + public String getServiceId(String registryName) { + if (CollectionUtils.isEmpty(registrationList)) { + return ""; + } + Optional> registration = + registrationList.stream().filter(r -> registryName.equals(r.name())).collect(Collectors.toList()) + .stream().findFirst(); + if (!registration.isPresent()) { + return ""; } + return registration.get().getMicroserviceInstance().getServiceId(); } - public String getAppId() { - assertPrimaryNotNull(); - return primary.getAppId(); + public Registration getPrimaryRegistration() { + return registrationList.get(0); } - public SwaggerLoader getSwaggerLoader() { - return swaggerLoader; + public void updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { + registrationList + .forEach(registration -> registration.updateMicroserviceInstanceStatus(status)); } - public void updateMicroserviceInstanceStatus( - MicroserviceInstanceStatus status) { + public void addProperty(String key, String value) { registrationList - .forEach(registration -> registration.updateMicroserviceInstanceStatus(status)); + .forEach(registration -> registration.addProperty(key, value)); } public void addSchema(String schemaId, String content) { @@ -126,208 +100,34 @@ public void addEndpoint(String endpoint) { .forEach(registration -> registration.addEndpoint(endpoint)); } - public void addBasePath(Collection basePaths) { - registrationList - .forEach(registration -> registration.addBasePath(basePaths)); - } - public void destroy() { - registrationList.forEach(LifeCycle::destroy); + registrationList.forEach(LambdaUtils.ignoreException(LifeCycle::destroy)); } public void run() { - EventManager.getEventBus().register(new AfterServiceInstanceRegistryHandler(registrationList.size())); registrationList.forEach(LifeCycle::run); } public void init() { - BeanUtils.addBeans(Registration.class, registrationList); - - initPrimary(); registrationList.forEach(LifeCycle::init); } - private void initPrimary() { - if (registrationList.isEmpty()) { - LOGGER.warn("No registration is enabled. Fix this if only in unit tests."); - primary = null; - } else { - primary = registrationList.get(0); - } - } - - /** - *

- * Register a third party service if not registered before, and set it's instances into - * {@linkplain StaticMicroserviceVersions StaticMicroserviceVersions}. - *

- *

- * The registered third party service has the same {@code appId} and {@code environment} as this microservice instance has, - * and there is only one schema represented by {@code schemaIntfCls}, whose name is the same as {@code microserviceName}. - *

- * - * This method is for initializing 3rd party service endpoint config. - * i.e. If this service has not been registered before, this service will be registered and the instances will be set; - * otherwise, NOTHING will happen. - * - * - * @param microserviceName name of the 3rd party service, and this param also specifies the schemaId - * @param version version of this 3rd party service - * @param instances the instances of this 3rd party service. Users only need to specify the endpoint information, other - * necessary information will be generate and set in the implementation of this method. - * @param schemaIntfCls the producer interface of the service. This interface is used to generate swagger schema and - * can also be used for the proxy interface of RPC style invocation. - */ - public void registerMicroserviceMapping(String microserviceName, String version, List instances, - Class schemaIntfCls) { - MicroserviceNameParser parser = new MicroserviceNameParser(getAppId(), microserviceName); - MicroserviceManager microserviceManager = DiscoveryManager.INSTANCE.getAppManager() - .getOrCreateMicroserviceManager(parser.getAppId()); - microserviceManager.getVersionsByName() - .computeIfAbsent(microserviceName, - svcName -> new StaticMicroserviceVersions(DiscoveryManager.INSTANCE.getAppManager(), parser.getAppId(), - microserviceName) - .init(schemaIntfCls, version, instances) - ); - } - - /** - * @see #registerMicroserviceMapping(String, String, List, Class) - * @param endpoints the endpoints of 3rd party service. Each of endpoints will be treated as a separated instance. - * Format of the endpoints is the same as the endpoints that ServiceComb microservices register in service-center, - * like {@code rest://127.0.0.1:8080} - */ - public void registerMicroserviceMappingByEndpoints(String microserviceName, String version, - List endpoints, Class schemaIntfCls) { - ArrayList microserviceInstances = new ArrayList<>(); - for (String endpoint : endpoints) { - MicroserviceInstance instance = new MicroserviceInstance(); - instance.setEndpoints(Collections.singletonList(endpoint)); - microserviceInstances.add(instance); - } - - registerMicroserviceMapping(microserviceName, version, microserviceInstances, schemaIntfCls); - } - - public static String getPublishAddress() { - String publicAddressSetting = - DynamicPropertyFactory.getInstance().getStringProperty(PUBLISH_ADDRESS, "").get(); - publicAddressSetting = publicAddressSetting.trim(); - if (publicAddressSetting.isEmpty()) { - return NetUtils.getHostAddress(); - } - - // placeholder is network interface name - if (publicAddressSetting.startsWith("{") && publicAddressSetting.endsWith("}")) { - return NetUtils - .ensureGetInterfaceAddress(publicAddressSetting.substring(1, publicAddressSetting.length() - 1)) - .getHostAddress(); - } - - return publicAddressSetting; - } - - public static String getPublishHostName() { - String publicAddressSetting = - DynamicPropertyFactory.getInstance().getStringProperty(PUBLISH_ADDRESS, "").get(); - publicAddressSetting = publicAddressSetting.trim(); - if (publicAddressSetting.isEmpty()) { - return NetUtils.getHostName(); - } - - if (publicAddressSetting.startsWith("{") && publicAddressSetting.endsWith("}")) { - return NetUtils - .ensureGetInterfaceAddress(publicAddressSetting.substring(1, publicAddressSetting.length() - 1)) - .getHostName(); - } - - return publicAddressSetting; - } - - /** - * In the case that listening address configured as 0.0.0.0, the publish address will be determined - * by the query result for the net interfaces. - * - * @return the publish address, or {@code null} if the param {@code address} is null. - */ - public static String getPublishAddress(String schema, String address) { - if (address == null) { - return address; - } - - try { - URI originalURI = new URI(schema + "://" + address); - IpPort ipPort = NetUtils.parseIpPort(originalURI); - if (ipPort == null) { - LOGGER.warn("address {} not valid.", address); - return null; - } - - IpPort publishIpPort = genPublishIpPort(schema, ipPort); - URIBuilder builder = new URIBuilder(originalURI); - return builder.setHost(publishIpPort.getHostOrIp()).setPort(publishIpPort.getPort()).build().toString(); - } catch (URISyntaxException e) { - LOGGER.warn("address {} not valid.", address); - return null; - } - } - - private static IpPort genPublishIpPort(String schema, IpPort ipPort) { - String publicAddressSetting = DynamicPropertyFactory.getInstance() - .getStringProperty(PUBLISH_ADDRESS, "") - .get(); - publicAddressSetting = publicAddressSetting.trim(); - - String publishPortKey = PUBLISH_PORT.replace("{transport_name}", schema); - int publishPortSetting = DynamicPropertyFactory.getInstance() - .getIntProperty(publishPortKey, 0) - .get(); - int publishPort = publishPortSetting == 0 ? ipPort.getPort() : publishPortSetting; - - if (publicAddressSetting.isEmpty()) { - InetSocketAddress socketAddress = ipPort.getSocketAddress(); - if (socketAddress.getAddress().isAnyLocalAddress()) { - String host = NetUtils.getHostAddress(); - if (Inet6Address.class.isInstance(socketAddress.getAddress())) { - host = NetUtils.getIpv6HostAddress(); - } - LOGGER.warn("address {}, auto select a host address to publish {}:{}, maybe not the correct one", - socketAddress, - host, - publishPort); - return new IpPort(host, publishPort); - } - - return ipPort; - } - - if (publicAddressSetting.startsWith("{") && publicAddressSetting.endsWith("}")) { - publicAddressSetting = NetUtils - .ensureGetInterfaceAddress( - publicAddressSetting.substring(1, publicAddressSetting.length() - 1)) - .getHostAddress(); - } - - - return new IpPort(publicAddressSetting, publishPort); - } - public String info() { StringBuilder result = new StringBuilder(); AtomicBoolean first = new AtomicBoolean(true); registrationList.forEach(registration -> { if (first.getAndSet(false)) { - result.append("App ID: " + registration.getAppId() + "\n"); - result.append("Service Name: " + registration.getMicroservice().getServiceName() + "\n"); - result.append("Version: " + registration.getMicroservice().getVersion() + "\n"); - result.append("Environment: " + registration.getMicroservice().getEnvironment() + "\n"); - result.append("Endpoints: " + getEndpoints(registration.getMicroserviceInstance().getEndpoints()) + "\n"); + result.append("App ID: ").append(registration.getMicroserviceInstance().getApplication()).append("\n"); + result.append("Service Name: ").append(registration.getMicroserviceInstance().getServiceName()).append("\n"); + result.append("Version: ").append(registration.getMicroserviceInstance().getVersion()).append("\n"); + result.append("Environment: ").append(registration.getMicroserviceInstance().getEnvironment()).append("\n"); + result.append("Endpoints: ").append(getEndpoints(registration.getMicroserviceInstance().getEndpoints())) + .append("\n"); result.append("Registration implementations:\n"); } - result.append(" name:" + registration.name() + "\n"); - result.append(" Service ID: " + registration.getMicroservice().getServiceId() + "\n"); - result.append(" Instance ID: " + registration.getMicroserviceInstance().getInstanceId() + "\n"); + result.append(" name:").append(registration.name()).append("\n"); + result.append(" Instance ID: ").append(registration.getMicroserviceInstance().getInstanceId()).append("\n"); }); return result.toString(); } @@ -335,32 +135,4 @@ public String info() { private String getEndpoints(List endpoints) { return JacksonFactory.CODEC.toString(endpoints); } - - public static class AfterServiceInstanceRegistryHandler { - private final AtomicInteger instanceRegisterCounter; - - AfterServiceInstanceRegistryHandler(int counter) { - instanceRegisterCounter = new AtomicInteger(counter); - } - - @Subscribe - @EnableExceptionPropagation - public void afterRegistryInstance(MicroserviceInstanceRegisteredEvent event) { - LOGGER.info("receive MicroserviceInstanceRegisteredEvent event, registration={}, instance id={}", - event.getRegistrationName(), - event.getInstanceId()); - - if (instanceRegisterCounter.decrementAndGet() > 0) { - return; - } - - EventManager.unregister(this); - - EventManager.getEventBus().post(new MicroserviceInstanceRegisteredEvent( - "Registration Manager", - null, - true - )); - } - } } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistryConfiguration.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistryConfiguration.java index b33c4f2b45b..7a1d72460cc 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistryConfiguration.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistryConfiguration.java @@ -17,14 +17,58 @@ package org.apache.servicecomb.registry; -import org.apache.servicecomb.registry.consumer.AppManager; +import java.util.List; + +import org.apache.servicecomb.registry.api.Discovery; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.registry.api.Registration; +import org.apache.servicecomb.registry.api.RegistrationInstance; +import org.apache.servicecomb.registry.discovery.DiscoveryTree; +import org.apache.servicecomb.registry.discovery.InstancePing; +import org.apache.servicecomb.registry.discovery.InstanceStatusDiscoveryFilter; +import org.apache.servicecomb.registry.discovery.MicroserviceInstanceCache; +import org.apache.servicecomb.registry.discovery.TelnetInstancePing; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration +@SuppressWarnings("unused") public class RegistryConfiguration { @Bean - public AppManager appManager() { - return DiscoveryManager.INSTANCE.getAppManager(); + public RegistrationManager scbRegistrationManager( + List> registrationList) { + return new RegistrationManager(registrationList); + } + + @Bean + public TelnetInstancePing scbTelnetInstancePing() { + return new TelnetInstancePing(); + } + + @Bean + public DiscoveryManager scbDiscoveryManager( + List> discoveryList, + List pingList) { + return new DiscoveryManager(discoveryList, pingList); + } + + @Bean + public DiscoveryTree scbDiscoveryTree(DiscoveryManager discoveryManager) { + return new DiscoveryTree(discoveryManager); + } + + @Bean + public InstanceStatusDiscoveryFilter scbInstanceStatusDiscoveryFilter() { + return new InstanceStatusDiscoveryFilter(); + } + + @Bean + public MicroserviceInstanceCache scbMicroserviceInstanceCache(DiscoveryManager discoveryManager) { + return new MicroserviceInstanceCache(discoveryManager); + } + + @Bean + public RegistrationId scbRegistrationId() { + return new RegistrationId(); } } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/AbstractDiscoveryInstance.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/AbstractDiscoveryInstance.java new file mode 100644 index 00000000000..08bcd6c02de --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/AbstractDiscoveryInstance.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.servicecomb.registry.api; + +public abstract class AbstractDiscoveryInstance implements DiscoveryInstance { + @Override + public int hashCode() { + return getInstanceId().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof DiscoveryInstance) { + return getInstanceId().equals(((DiscoveryInstance) obj).getInstanceId()); + } + return false; + } +} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/DataCenterInfo.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/DataCenterInfo.java similarity index 84% rename from foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/DataCenterInfo.java rename to foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/DataCenterInfo.java index b178b3181ca..7c6496e4b91 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/DataCenterInfo.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/DataCenterInfo.java @@ -14,16 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.registry.api.registry; -public class DataCenterInfo { +package org.apache.servicecomb.registry.api; +public class DataCenterInfo { private String name; private String region; private String availableZone; + public DataCenterInfo() { + + } + + public DataCenterInfo(String name, String region, String availableZone) { + this.name = name; + this.region = region; + this.availableZone = availableZone; + } + public String getName() { return name; } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Discovery.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Discovery.java index cf7029eebaa..0c57ed6b605 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Discovery.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Discovery.java @@ -17,61 +17,54 @@ package org.apache.servicecomb.registry.api; -import java.util.Collection; import java.util.List; import org.apache.servicecomb.foundation.common.utils.SPIEnabled; import org.apache.servicecomb.foundation.common.utils.SPIOrder; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstances; /** * This is the core service discovery interface.
*/ -public interface Discovery extends SPIEnabled, SPIOrder, LifeCycle { - /** - * get Microservice
- * - * Life Cycle:This method is called anytime after run. - */ - Microservice getMicroservice(String microserviceId); +public interface Discovery extends SPIEnabled, SPIOrder, LifeCycle { + interface InstanceChangedListener { + /** + * Called by Discovery Implementations when instance list changed. + * @param registryName Name of the calling discovery implementation + * @param application Microservice application + * @param serviceName Microservice name + * @param updatedInstances The latest updated instances. + */ + void onInstanceChanged(String registryName, String application, String serviceName, List updatedInstances); + } + + String name(); /** - * get all Microservices
- * - * Life Cycle:This method is called anytime after run. + * If this implementation enabled for this microservice. */ - List getAllMicroservices(); + boolean enabled(String application, String serviceName); /** - * get schema content
+ * Find all instances. * * Life Cycle:This method is called anytime after run. + * + * @param application application + * @param serviceName microservice name + * @return all instances match the criteria. */ - String getSchema(String microserviceId, Collection instances, String schemaId); + List findServiceInstances(String application, String serviceName); /** - * get MicroserviceInstance
+ * Find all services of this application. * - * Life Cycle:This method is called anytime after run. + * @param application application + * @return all services match the criteria. */ - MicroserviceInstance getMicroserviceInstance(String serviceId, String instanceId); + List findServices(String application); /** - * Find all instances. Implementations can use gerRevision to retrieve the - * latest instances changed.
- * - * Life Cycle:This method is called anytime after run. - * - * @param appId application id - * @param serviceName microservice name - * @param versionRule literal version rule. e.g. 1.0.0, 1.0.0+, [1.0.0, 2.0.0) - * @return all instances match the criteria. + * Discovery can call InstanceChangedListener when instance get changed. */ - default MicroserviceInstances findServiceInstances(String appId, String serviceName, String versionRule) { - return null; - } - - String name(); + void setInstanceChangedListener(InstanceChangedListener instanceChangedListener); } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/DiscoveryInstance.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/DiscoveryInstance.java new file mode 100644 index 00000000000..f981aa349e6 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/DiscoveryInstance.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.servicecomb.registry.api; + +/** + * Microserivce instance discovery object. + */ +public interface DiscoveryInstance extends MicroserviceInstance { + /** + * Microservice instance status. + */ + MicroserviceInstanceStatus getStatus(); + + /** + * This name of Discovery Implementation. + */ + String getRegistryName(); +} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstance.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstance.java new file mode 100644 index 00000000000..c5c872beefd --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstance.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.servicecomb.registry.api; + +import java.util.List; +import java.util.Map; + +/** + * Standard information used for microservice instance registration and discovery. + */ +public interface MicroserviceInstance { + /** + * Environment(Required): Used for logic separation of microservice instance. Only + * microservice instance with same environment can discovery each other. + */ + String getEnvironment(); + + /** + * Application(Required): Used for logic separation of microservice instance. Only + * microservice instance with same application can discovery each other. + */ + String getApplication(); + + /** + * Service Name(Required): Unique identifier for microservice. + */ + String getServiceName(); + + /** + * Service Name Alias(Optional): Unique identifier for microservice. + * This alias is used by registry implementation to support rename + * of a microservice, e.g. old consumers use old service name can + * find a renamed microservice service. + */ + String getAlias(); + + /** + * Service Version(Required): version of this microservice. + */ + String getVersion(); + + /** + * Data center info(Optional). + */ + DataCenterInfo getDataCenterInfo(); + + /** + * Service Description(Optional) + */ + String getDescription(); + + /** + * Service Properties(Optional) + */ + Map getProperties(); + + /** + * Service Schemas(Optional): Open API information. + */ + Map getSchemas(); + + /** + * Service endpoints(Optional). + */ + List getEndpoints(); + + /** + * Microservice instance id(Required). This id can be generated when microservice instance is starting + * or assigned by registry implementation. + * + * When microservice instance is restarted, this id should be changed. + */ + String getInstanceId(); + + /** + * Microservice service id(Optional). This is used for service center, other implementations may not + * support service id. + */ + default String getServiceId() { + return ""; + } + + /** + * Service status(Required): status of this microservice. + * + */ + MicroserviceInstanceStatus getStatus(); +} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceInstanceStatus.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstanceStatus.java similarity index 89% rename from foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceInstanceStatus.java rename to foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstanceStatus.java index 536feadca7e..4bde42dee91 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceInstanceStatus.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstanceStatus.java @@ -15,16 +15,12 @@ * limitations under the License. */ -package org.apache.servicecomb.registry.api.registry; +package org.apache.servicecomb.registry.api; -/** - * Created by on 2016/12/5. - */ public enum MicroserviceInstanceStatus { STARTING, TESTING, UP, - OUTOFSERVICE, DOWN, UNKNOWN } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Registration.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Registration.java index 5f72f0e0537..014da5f147b 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Registration.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Registration.java @@ -17,67 +17,48 @@ package org.apache.servicecomb.registry.api; -import java.util.Collection; - import org.apache.servicecomb.foundation.common.utils.SPIEnabled; import org.apache.servicecomb.foundation.common.utils.SPIOrder; -import org.apache.servicecomb.registry.api.registry.BasePath; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstanceStatus; + /** - * This is the core service registration interface.
+ * This is the core service registration interface. */ -public interface Registration extends SPIEnabled, SPIOrder, LifeCycle { +public interface Registration extends SPIEnabled, SPIOrder, LifeCycle { String name(); /** - * get MicroserviceInstance
- * - * Life Cycle:This method is called anytime after run. - */ - MicroserviceInstance getMicroserviceInstance(); - - /** - * get Microservice
- * - * Life Cycle:This method is called anytime after run. - */ - Microservice getMicroservice(); - - /** - * get application id
- * + * get MicroserviceInstance + *

* Life Cycle:This method is called anytime after run. */ - String getAppId(); + R getMicroserviceInstance(); /** - * update MicroserviceInstance status
- * + * update MicroserviceInstance status + *

* Life Cycle:This method is called anytime after run. */ boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status); /** - * adding schemas to Microservice
- * + * adding schemas to Microservice + *

* Life Cycle:This method is called after init and before run. */ void addSchema(String schemaId, String content); /** - * adding endpoints to MicroserviceInstance
- * + * adding endpoints to MicroserviceInstance + *

* Life Cycle:This method is called after init and before run. */ void addEndpoint(String endpoint); /** - * adding BasePath to MicroserviceInstance
- * + * adding property to MicroserviceInstance + *

* Life Cycle:This method is called after init and before run. */ - void addBasePath(Collection basePaths); + void addProperty(String key, String value); } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/RegistrationInstance.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/RegistrationInstance.java new file mode 100644 index 00000000000..a7d7cafda88 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/RegistrationInstance.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.servicecomb.registry.api; + +/** + * Microserivce instance registration object. + */ +public interface RegistrationInstance extends MicroserviceInstance { +} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Versions.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Versions.java deleted file mode 100644 index aca4a674b78..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Versions.java +++ /dev/null @@ -1,23 +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.servicecomb.registry.api; - -import java.util.Map; - -public interface Versions { - Map loadVersion(); -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/CreateMicroserviceEvent.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/CreateMicroserviceEvent.java deleted file mode 100644 index 522b2db3a77..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/CreateMicroserviceEvent.java +++ /dev/null @@ -1,31 +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.servicecomb.registry.api.event; - -import org.apache.servicecomb.registry.consumer.MicroserviceVersions; - -public class CreateMicroserviceEvent { - private final MicroserviceVersions microserviceVersions; - - public CreateMicroserviceEvent(MicroserviceVersions microserviceVersions) { - this.microserviceVersions = microserviceVersions; - } - - public MicroserviceVersions getMicroserviceVersions() { - return microserviceVersions; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/CreateMicroserviceVersionEvent.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/CreateMicroserviceVersionEvent.java deleted file mode 100644 index 2c37f110fe7..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/CreateMicroserviceVersionEvent.java +++ /dev/null @@ -1,31 +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.servicecomb.registry.api.event; - -import org.apache.servicecomb.registry.consumer.MicroserviceVersion; - -public class CreateMicroserviceVersionEvent { - private final MicroserviceVersion microserviceVersion; - - public CreateMicroserviceVersionEvent(MicroserviceVersion microserviceVersion) { - this.microserviceVersion = microserviceVersion; - } - - public MicroserviceVersion getMicroserviceVersion() { - return microserviceVersion; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/DestroyMicroserviceEvent.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/DestroyMicroserviceEvent.java deleted file mode 100644 index ffe4da703b4..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/DestroyMicroserviceEvent.java +++ /dev/null @@ -1,31 +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.servicecomb.registry.api.event; - -import org.apache.servicecomb.registry.consumer.MicroserviceVersions; - -public class DestroyMicroserviceEvent { - private final MicroserviceVersions microserviceVersions; - - public DestroyMicroserviceEvent(MicroserviceVersions microserviceVersions) { - this.microserviceVersions = microserviceVersions; - } - - public MicroserviceVersions getMicroserviceVersions() { - return microserviceVersions; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/DestroyMicroserviceVersionEvent.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/DestroyMicroserviceVersionEvent.java deleted file mode 100644 index 26371335952..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/DestroyMicroserviceVersionEvent.java +++ /dev/null @@ -1,31 +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.servicecomb.registry.api.event; - -import org.apache.servicecomb.registry.consumer.MicroserviceVersion; - -public class DestroyMicroserviceVersionEvent { - private final MicroserviceVersion microserviceVersion; - - public DestroyMicroserviceVersionEvent(MicroserviceVersion microserviceVersion) { - this.microserviceVersion = microserviceVersion; - } - - public MicroserviceVersion getMicroserviceVersion() { - return microserviceVersion; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/MicroserviceInstanceChangedEvent.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/MicroserviceInstanceChangedEvent.java deleted file mode 100644 index c850be99e62..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/MicroserviceInstanceChangedEvent.java +++ /dev/null @@ -1,57 +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.servicecomb.registry.api.event; - -import org.apache.servicecomb.registry.api.MicroserviceKey; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.api.registry.WatchAction; - -/** - * Created by on 2016/12/25. - */ -public class MicroserviceInstanceChangedEvent { - private WatchAction action; - - private MicroserviceKey key; - - private MicroserviceInstance instance; - - public MicroserviceInstance getInstance() { - return instance; - } - - public void setInstance(MicroserviceInstance instance) { - this.instance = instance; - } - - public MicroserviceKey getKey() { - return key; - } - - public void setKey(MicroserviceKey key) { - this.key = key; - } - - public WatchAction getAction() { - return action; - } - - public void setAction(WatchAction action) { - this.action = action; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/MicroserviceInstanceRegisteredEvent.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/MicroserviceInstanceRegisteredEvent.java deleted file mode 100644 index 71ce6929054..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/MicroserviceInstanceRegisteredEvent.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.servicecomb.registry.api.event; - -/** - * when registration is ready, should post this event. - */ -public class MicroserviceInstanceRegisteredEvent { - private final String registrationName; - - private final String instanceId; - - // If this event is sent by RegistrationManager, which means all Registration are successful. - private final boolean registrationManager; - - public MicroserviceInstanceRegisteredEvent(String registrationName, String instanceId, - boolean registrationManager) { - this.registrationName = registrationName; - this.instanceId = instanceId; - this.registrationManager = registrationManager; - } - - public String getInstanceId() { - return instanceId; - } - - public String getRegistrationName() { - return registrationName; - } - - public boolean isRegistrationManager() { - return registrationManager; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/ServiceCenterEventBus.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/ServiceCenterEventBus.java deleted file mode 100644 index 204ee726249..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/event/ServiceCenterEventBus.java +++ /dev/null @@ -1,30 +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.servicecomb.registry.api.event; - -import org.apache.servicecomb.foundation.common.event.SimpleEventBus; - -import com.google.common.eventbus.EventBus; - -public final class ServiceCenterEventBus { - private static final EventBus eventBus = new SimpleEventBus(); - - public static EventBus getEventBus() { - return eventBus; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/BasePath.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/BasePath.java deleted file mode 100644 index a83e9c859b7..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/BasePath.java +++ /dev/null @@ -1,48 +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.servicecomb.registry.api.registry; - -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -/** - * Created by on 2017/3/29. - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class BasePath { - private String path; - - private Map property; - - public Map getProperty() { - return property; - } - - public void setProperty(Map property) { - this.property = property; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/Framework.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/Framework.java deleted file mode 100644 index 04c970b5d27..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/Framework.java +++ /dev/null @@ -1,43 +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.servicecomb.registry.api.registry; - -/** - * Created by on 2017/12/20. - */ -public class Framework { - private String name; - - private String version; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/FrameworkVersions.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/FrameworkVersions.java deleted file mode 100644 index e655119b7a2..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/FrameworkVersions.java +++ /dev/null @@ -1,34 +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.servicecomb.registry.api.registry; - -import java.util.ServiceLoader; - -import org.apache.servicecomb.registry.api.Versions; - -public class FrameworkVersions { - private static final ServiceLoader frameworkVersions = ServiceLoader.load(Versions.class); - - public static String allVersions() { - StringBuffer sb = new StringBuffer(); - frameworkVersions.forEach(version -> version.loadVersion().forEach((key, value) -> sb.append(key).append(":").append(value).append(";"))); - if (sb.length() > 0) { - sb.setLength(sb.length() - 1); - } - return sb.toString(); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceFactory.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceFactory.java deleted file mode 100644 index 16b714728c4..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceFactory.java +++ /dev/null @@ -1,99 +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.servicecomb.registry.api.registry; - -import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.APP_MAPPING; -import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_DEFAULT_REGISTER_BY; -import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_FRAMEWORK_DEFAULT_NAME; -import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.SERVICE_MAPPING; -import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.VERSION_MAPPING; - -import java.util.Map; - -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.EnvironmentConfiguration; -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.config.BootStrapProperties; -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.registry.config.ConfigurePropertyUtils; -import org.apache.servicecomb.registry.config.MicroservicePropertiesLoader; -import org.apache.servicecomb.foundation.common.Version; - -public class MicroserviceFactory { - public Microservice create() { - return create(ConfigUtil.createLocalConfig()); - } - - public Microservice create(Configuration configuration) { - Microservice microservice = createMicroserviceFromConfiguration(configuration); - microservice.setInstance(MicroserviceInstance.createFromDefinition(configuration)); - return microservice; - } - - private Microservice createMicroserviceFromConfiguration(Configuration configuration) { - Microservice microservice = new Microservice(); - - EnvironmentConfiguration envConfig = new EnvironmentConfiguration(); - if (!StringUtils.isEmpty(envConfig.getString(APP_MAPPING)) && - !StringUtils.isEmpty(envConfig.getString(envConfig.getString(APP_MAPPING)))) { - microservice.setAppId(envConfig.getString(envConfig.getString(APP_MAPPING))); - } else { - microservice.setAppId(BootStrapProperties.readApplication(configuration)); - } - if (!StringUtils.isEmpty(envConfig.getString(SERVICE_MAPPING)) && - !StringUtils.isEmpty(envConfig.getString(envConfig.getString(SERVICE_MAPPING)))) { - microservice.setServiceName(envConfig.getString(envConfig.getString(SERVICE_MAPPING))); - } else { - microservice.setServiceName(BootStrapProperties.readServiceName(configuration)); - } - String version; - if (!StringUtils.isEmpty(envConfig.getString(VERSION_MAPPING)) && - !StringUtils.isEmpty(envConfig.getString(envConfig.getString(VERSION_MAPPING)))) { - version = envConfig.getString(envConfig.getString(VERSION_MAPPING)); - } else { - version = BootStrapProperties.readServiceVersion(configuration); - } - // just check version format - new Version(version); - microservice.setVersion(version); - - microservice.setDescription(BootStrapProperties.readServiceDescription(configuration)); - microservice.setLevel(BootStrapProperties.readServiceRole(configuration)); - microservice.setPaths(ConfigurePropertyUtils.getMicroservicePaths(configuration)); - Map propertiesMap = MicroservicePropertiesLoader.INSTANCE.loadProperties(configuration); - microservice.setProperties(propertiesMap); - microservice.setEnvironment(BootStrapProperties.readServiceEnvironment(configuration)); - - // set alias name when allow cross app - if (microservice.allowCrossApp()) { - microservice.setAlias(Microservice.generateAbsoluteMicroserviceName(microservice.getAppId(), - microservice.getServiceName())); - } - - microservice.setFramework(createFramework()); - microservice.setRegisterBy(CONFIG_DEFAULT_REGISTER_BY); - - return microservice; - } - - private Framework createFramework() { - Framework framework = new Framework(); - framework.setName(CONFIG_FRAMEWORK_DEFAULT_NAME); - framework.setVersion(FrameworkVersions.allVersions()); - return framework; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceInstance.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceInstance.java deleted file mode 100644 index fdab707d6db..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceInstance.java +++ /dev/null @@ -1,209 +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.servicecomb.registry.api.registry; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.configuration.Configuration; -import org.apache.servicecomb.config.BootStrapProperties; -import org.apache.servicecomb.registry.config.InstancePropertiesLoader; -import org.apache.servicecomb.registry.definition.DefinitionConst; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.netflix.config.DynamicPropertyFactory; - -/** - * Created by on 2016/12/5. - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class MicroserviceInstance { - // even disconnected from service center - // instanceId will not be changed - // when register to service center again, use the old instanceId. - private String instanceId; - - // service center rule: max length: 64 - private String serviceId; - - private List endpoints = new ArrayList<>(); - - private String hostName; - - private MicroserviceInstanceStatus status = MicroserviceInstanceStatus.UP; - - private Map properties = new HashMap<>(); // reserved key list: region|az|stage|group - - private HealthCheck healthCheck; - - private String stage; - - private DataCenterInfo dataCenterInfo; - - private String timestamp; - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("instanceId=" + instanceId + ";"); - sb.append("serviceId=" + serviceId + ";"); - sb.append("status=" + status + ";"); - sb.append("endpoints=" + endpoints.toString()); - return sb.toString(); - } - - public String getTimestamp() { - return timestamp; - } - - public void setTimestamp(String timestamp) { - this.timestamp = timestamp; - } - - public String getInstanceId() { - return instanceId; - } - - public void setInstanceId(String instanceId) { - this.instanceId = instanceId; - } - - public MicroserviceInstance instanceId(String instanceId) { - this.instanceId = instanceId; - return this; - } - - public String getServiceId() { - return serviceId; - } - - public void setServiceId(String serviceId) { - this.serviceId = serviceId; - } - - public MicroserviceInstance serviceId(String serviceId) { - this.serviceId = serviceId; - return this; - } - - public String getHostName() { - return hostName; - } - - public void setHostName(String hostName) { - this.hostName = hostName; - } - - public List getEndpoints() { - return endpoints; - } - - public void setEndpoints(List endpoints) { - this.endpoints = endpoints; - } - - public MicroserviceInstanceStatus getStatus() { - return status; - } - - public void setStatus(MicroserviceInstanceStatus status) { - this.status = status; - } - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - - public HealthCheck getHealthCheck() { - return healthCheck; - } - - public void setHealthCheck(HealthCheck healthCheck) { - this.healthCheck = healthCheck; - } - - @Deprecated - public String getStage() { - return stage; - } - - @Deprecated - public void setStage(String stage) { - this.stage = stage; - } - - public DataCenterInfo getDataCenterInfo() { - return dataCenterInfo; - } - - @Override - public int hashCode() { - return this.instanceId.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof MicroserviceInstance) { - return this.instanceId.equals(((MicroserviceInstance) obj).instanceId); - } - return false; - } - - public void setDataCenterInfo(DataCenterInfo dataCenterInfo) { - this.dataCenterInfo = dataCenterInfo; - } - - // Some properties of microservice instance are dynamic changed, not cover them all now. - public static MicroserviceInstance createFromDefinition(Configuration configuration) { - MicroserviceInstance microserviceInstance = new MicroserviceInstance(); - // default hard coded values - microserviceInstance.setStage(DefinitionConst.DEFAULT_STAGE); - microserviceInstance.setStatus(MicroserviceInstanceStatus - .valueOf(BootStrapProperties.readServiceInstanceInitialStatus())); - HealthCheck healthCheck = new HealthCheck(); - healthCheck.setMode(HealthCheckMode.HEARTBEAT); - microserviceInstance.setHealthCheck(healthCheck); - - // load properties - Map propertiesMap = InstancePropertiesLoader.INSTANCE.loadProperties(configuration); - microserviceInstance.setProperties(propertiesMap); - - // load data center information - loadDataCenterInfo(microserviceInstance); - return microserviceInstance; - } - - private static void loadDataCenterInfo(MicroserviceInstance microserviceInstance) { - String dataCenterName = DynamicPropertyFactory.getInstance() - .getStringProperty("servicecomb.datacenter.name", "default") - .get(); - DataCenterInfo dataCenterInfo = new DataCenterInfo(); - dataCenterInfo.setName(dataCenterName); - dataCenterInfo.setRegion(DynamicPropertyFactory.getInstance(). - getStringProperty("servicecomb.datacenter.region", "default").get()); - dataCenterInfo.setAvailableZone(DynamicPropertyFactory.getInstance(). - getStringProperty("servicecomb.datacenter.availableZone", "default").get()); - microserviceInstance.setDataCenterInfo(dataCenterInfo); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceStatus.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceStatus.java deleted file mode 100644 index ca043fd9ed7..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/MicroserviceStatus.java +++ /dev/null @@ -1,27 +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.servicecomb.registry.api.registry; - -/** - * Created by on 2016/12/5. - */ -public enum MicroserviceStatus { - UNKNOWN, - UP, - DOWN -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/ServiceCombVersion.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/ServiceCombVersion.java deleted file mode 100644 index 61b8ba0a587..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/ServiceCombVersion.java +++ /dev/null @@ -1,33 +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.servicecomb.registry.api.registry; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.servicecomb.registry.api.Versions; - -public class ServiceCombVersion implements Versions { - - @Override - public Map loadVersion() { - Map map = new HashMap<>(); - map.put("ServiceComb", this.getClass().getPackage().getImplementationVersion()); - - return map; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/WatchAction.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/WatchAction.java deleted file mode 100644 index bc731f4722f..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/registry/WatchAction.java +++ /dev/null @@ -1,43 +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.servicecomb.registry.api.registry; - -import com.fasterxml.jackson.annotation.JsonValue; - -/** - * Created by on 2017/2/21. - */ -public enum WatchAction { - CREATE("CREATE"), - UPDATE("UPDATE"), - // When SC send an EXPIRE action, which means client should clean up all local instance cache and fetch again. - // This usually happens when SC adds new WHITE/BLACK rules or changes TAGS of instance - EXPIRE("EXPIRE"), - DELETE("DELETE"); - - private final String name; - - WatchAction(String name) { - this.name = name; - } - - @JsonValue - public String getName() { - return name; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/CacheEndpoint.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/CacheEndpoint.java deleted file mode 100644 index a36962811de..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/CacheEndpoint.java +++ /dev/null @@ -1,44 +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.servicecomb.registry.cache; - -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; - -public class CacheEndpoint { - // 所属的服务实例 - private final MicroserviceInstance instance; - - private final String endpoint; - - public CacheEndpoint(String endpoint, MicroserviceInstance instance) { - this.endpoint = endpoint; - this.instance = instance; - } - - public String getEndpoint() { - return endpoint; - } - - public MicroserviceInstance getInstance() { - return instance; - } - - public String toString() { - return endpoint; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/InstanceCache.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/InstanceCache.java deleted file mode 100644 index 73c651a1b40..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/InstanceCache.java +++ /dev/null @@ -1,137 +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.servicecomb.registry.cache; - -import java.net.URI; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.servicecomb.foundation.common.cache.VersionedCache; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstanceStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 缓存指定微服务的所有实例 - * 当实例状态变化时,需要重新创建InstanceCache,由外部控制 - */ -public class InstanceCache { - private static final Logger LOGGER = LoggerFactory.getLogger(InstanceCache.class); - - // 引入这个原子变量,是为了避免一个服务长期不用,缓存被删除,下次再初始化的初始版本号,有极小概率等于被删除前的版本号的问题 - // 如果相等,可能会导致其他没感知删除的模块,在更新流程上,遇到问题 - private static final AtomicInteger VERSION = new AtomicInteger(); - - private final int cacheVersion; - - private final String appId; - - private final String microserviceName; - - // 1.0或1.0+或latest等等,只是规则,不一定表示一个确定的版本 - private final String microserviceVersionRule; - - // key为instanceId - private final Map instanceMap; - - private final VersionedCache versionedCache; - - // 缓存CacheEndpoint - private volatile Map> transportMap; - - private final Object lockObj = new Object(); - - /** - * 用于初始化场景 - */ - public InstanceCache(String appId, String microserviceName, String microserviceVersionRule, - Map instanceMap) { - cacheVersion = VERSION.getAndIncrement(); - this.appId = appId; - this.microserviceName = microserviceName; - this.microserviceVersionRule = microserviceVersionRule; - this.instanceMap = instanceMap; - this.versionedCache = new VersionedCache() - .name(microserviceVersionRule) - .autoCacheVersion() - .data(instanceMap); - } - - public VersionedCache getVersionedCache() { - return versionedCache; - } - - public boolean cacheChanged(InstanceCache newCache) { - return newCache != null - && newCache.instanceMap != null - && newCache.cacheVersion != cacheVersion; - } - - public Map> getOrCreateTransportMap() { - if (transportMap == null) { - synchronized (lockObj) { - if (transportMap == null) { - transportMap = createTransportMap(); - } - } - } - return transportMap; - } - - protected Map> createTransportMap() { - Map> transportMap = new HashMap<>(); - for (MicroserviceInstance instance : instanceMap.values()) { - // This is only used for service center, not change it now - if (instance.getStatus() != MicroserviceInstanceStatus.UP) { - continue; - } - for (String endpoint : instance.getEndpoints()) { - try { - URI uri = URI.create(endpoint); - String transportName = uri.getScheme(); - - List cacheEndpointList = transportMap.computeIfAbsent(transportName, k -> new ArrayList<>()); - cacheEndpointList.add(new CacheEndpoint(endpoint, instance)); - } catch (Exception e) { - LOGGER.warn("unrecognized address find, ignore " + endpoint); - } - } - } - return transportMap; - } - - public Map getInstanceMap() { - return instanceMap; - } - - public String getMicroserviceName() { - return microserviceName; - } - - public String getMicroserviceVersionRule() { - return microserviceVersionRule; - } - - public String getAppId() { - return appId; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/InstanceCacheManager.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/InstanceCacheManager.java deleted file mode 100644 index 7daa947900a..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/InstanceCacheManager.java +++ /dev/null @@ -1,26 +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.servicecomb.registry.cache; - -import org.apache.servicecomb.foundation.common.cache.VersionedCache; - -public interface InstanceCacheManager { - InstanceCache getOrCreate(String appId, String microserviceName, String microserviceVersionRule); - - VersionedCache getOrCreateVersionedCache(String appId, String microserviceName, String microserviceVersionRule); -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/InstanceCacheManagerNew.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/InstanceCacheManagerNew.java deleted file mode 100644 index d3608c65e85..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/InstanceCacheManagerNew.java +++ /dev/null @@ -1,42 +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.servicecomb.registry.cache; - -import org.apache.servicecomb.foundation.common.cache.VersionedCache; -import org.apache.servicecomb.registry.consumer.AppManager; - -public class InstanceCacheManagerNew implements InstanceCacheManager { - private final AppManager appManager; - - public InstanceCacheManagerNew(AppManager appManager) { - this.appManager = appManager; - } - - @Override - public InstanceCache getOrCreate(String appId, String microserviceName, String microserviceVersionRule) { - return appManager.getOrCreateMicroserviceVersionRule(appId, microserviceName, microserviceVersionRule) - .getInstanceCache(); - } - - @Override - public VersionedCache getOrCreateVersionedCache(String appId, String microserviceName, - String microserviceVersionRule) { - return appManager.getOrCreateMicroserviceVersionRule(appId, microserviceName, microserviceVersionRule) - .getVersionedCache(); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/MicroserviceInstanceCache.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/MicroserviceInstanceCache.java deleted file mode 100644 index 07fcec60f65..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/cache/MicroserviceInstanceCache.java +++ /dev/null @@ -1,82 +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.servicecomb.registry.cache; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; - -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.DiscoveryManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.util.concurrent.UncheckedExecutionException; - -/** - * 微服务实例缓存 key为:serviceId@instanceId 缓存limit:1000 缓存老化策略:30分钟没有访问就过期。 - * - */ -public class MicroserviceInstanceCache { - - private static final Logger logger = LoggerFactory.getLogger(MicroserviceInstanceCache.class); - - private static final Cache instances = CacheBuilder.newBuilder() - .maximumSize(1000) - .expireAfterAccess(30, TimeUnit.MINUTES) - .build(); - - private static final Cache microservices = CacheBuilder.newBuilder() - .maximumSize(1000) - .expireAfterAccess(30, TimeUnit.MINUTES) - .build(); - - public static Microservice getOrCreate(String serviceId) { - try { - return microservices.get(serviceId, () -> { - Microservice microservice = DiscoveryManager.INSTANCE.getMicroservice(serviceId); - if (microservice == null) { - throw new IllegalArgumentException("service id not exists."); - } - return microservice; - }); - } catch (ExecutionException | UncheckedExecutionException e) { - logger.error("get microservice from cache failed, {}, {}", serviceId, e.getMessage()); - return null; - } - } - - public static MicroserviceInstance getOrCreate(String serviceId, String instanceId) { - try { - String key = String.format("%s@%s", serviceId, instanceId); - return instances.get(key, () -> { - MicroserviceInstance instance = DiscoveryManager.INSTANCE.getMicroserviceInstance(serviceId, instanceId); - if (instance == null) { - throw new IllegalArgumentException("instance id not exists."); - } - return instance; - }); - } catch (ExecutionException | UncheckedExecutionException e) { - logger.error("get microservice instance from cache failed, {}, {}", String.format("%s@%s", serviceId, instanceId), - e.getMessage()); - return null; - } - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/config/AbstractPropertiesLoader.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/config/AbstractPropertiesLoader.java deleted file mode 100644 index 4cee6875f12..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/config/AbstractPropertiesLoader.java +++ /dev/null @@ -1,79 +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.servicecomb.registry.config; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.configuration.Configuration; -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.registry.api.PropertyExtended; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Loading microservice properties - */ -public abstract class AbstractPropertiesLoader { - private static final Logger LOGGER = LoggerFactory.getLogger(AbstractPropertiesLoader.class); - - public Map loadProperties(Configuration configuration) { - Map propertiesMap = new HashMap<>(); - loadPropertiesFromConfigMap(configuration, propertiesMap); - loadPropertiesFromExtendedClass(configuration, propertiesMap); - - return propertiesMap; - } - - protected abstract Map readProperties(Configuration configuration); - - protected abstract String readPropertiesExtendedClass(Configuration configuration); - - private void loadPropertiesFromConfigMap(Configuration configuration, Map propertiesMap) { - propertiesMap.putAll(readProperties(configuration)); - } - - private void loadPropertiesFromExtendedClass(Configuration configuration, Map propertiesMap) { - String extendedPropertyClass = readPropertiesExtendedClass(configuration); - - if (StringUtils.isEmpty(extendedPropertyClass)) { - return; - } - - try { - Class classExternalProperty = Class.forName(extendedPropertyClass); - if (!PropertyExtended.class.isAssignableFrom(classExternalProperty)) { - String errMsg = String.format( - "Define propertyExtendedClass %s in yaml, but not implement the interface PropertyExtended.", - extendedPropertyClass); - LOGGER.error(errMsg); - throw new Error(errMsg); - } - - PropertyExtended instance = (PropertyExtended) classExternalProperty.getDeclaredConstructor().newInstance(); - Map extendedPropertiesMap = instance.getExtendedProperties(); - if (extendedPropertiesMap != null && !extendedPropertiesMap.isEmpty()) { - propertiesMap.putAll(extendedPropertiesMap); - } - } catch (ReflectiveOperationException e) { - String errMsg = "Fail to create instance of class: " + extendedPropertyClass; - LOGGER.error(errMsg); - throw new Error(errMsg, e); - } - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/config/ConfigurePropertyUtils.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/config/ConfigurePropertyUtils.java deleted file mode 100644 index 7a9f38b6f5d..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/config/ConfigurePropertyUtils.java +++ /dev/null @@ -1,86 +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.servicecomb.registry.config; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.apache.commons.configuration.Configuration; -import org.apache.commons.lang.StringUtils; -import org.apache.servicecomb.config.BootStrapProperties; -import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; -import org.apache.servicecomb.registry.api.registry.BasePath; -import org.apache.servicecomb.registry.definition.DefinitionConst; - -import com.fasterxml.jackson.databind.type.TypeFactory; -import com.netflix.config.DynamicPropertyFactory; - -import io.vertx.core.json.jackson.DatabindCodec; - -public final class ConfigurePropertyUtils { - private ConfigurePropertyUtils() { - } - - /** - * 获取key包含prefix前缀的所有配置项 - */ - public static Map getPropertiesWithPrefix(String prefix) { - Object config = DynamicPropertyFactory.getBackingConfigurationSource(); - if (!Configuration.class.isInstance(config)) { - return new HashMap<>(); - } - - return getPropertiesWithPrefix((Configuration) config, prefix); - } - - // caller ensure configuration is valid - public static Map getPropertiesWithPrefix(Configuration configuration, String prefix) { - Map propertiesMap = new HashMap<>(); - - Iterator keysIterator = configuration.getKeys(prefix); - while (keysIterator.hasNext()) { - String key = keysIterator.next(); - propertiesMap.put(key.substring(prefix.length() + 1), String.valueOf(configuration.getProperty(key))); - } - return propertiesMap; - } - - public static List getMicroservicePaths(Configuration configuration) { - List configPaths = BootStrapProperties.readServicePaths(configuration); - List basePaths = DatabindCodec.mapper().convertValue( - configPaths, - TypeFactory.defaultInstance().constructCollectionType(List.class, BasePath.class) - ); - for (BasePath basePath : basePaths) { - basePath.setPath(buildPath(basePath.getPath())); - } - return basePaths; - } - - private static String buildPath(String path) { - String prefix = ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX); - if (StringUtils.isNotEmpty(prefix)) { - if (!path.startsWith(prefix)) { - path = prefix + path; - } - } - return path; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/config/MicroservicePropertiesLoader.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/config/MicroservicePropertiesLoader.java deleted file mode 100644 index 2ac8865df17..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/config/MicroservicePropertiesLoader.java +++ /dev/null @@ -1,41 +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.servicecomb.registry.config; - -import java.util.Map; - -import org.apache.commons.configuration.Configuration; -import org.apache.servicecomb.config.BootStrapProperties; - -public final class MicroservicePropertiesLoader extends AbstractPropertiesLoader { - - public static final MicroservicePropertiesLoader INSTANCE = new MicroservicePropertiesLoader(); - - private MicroservicePropertiesLoader() { - } - - @Override - protected Map readProperties(Configuration configuration) { - return BootStrapProperties.readServiceProperties(configuration); - } - - @Override - protected String readPropertiesExtendedClass(Configuration configuration) { - return BootStrapProperties.readServiceExtendedClass(configuration); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/config/ServiceRegistryCommonConfig.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/config/ServiceRegistryCommonConfig.java deleted file mode 100644 index f307164c716..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/config/ServiceRegistryCommonConfig.java +++ /dev/null @@ -1,40 +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.servicecomb.registry.config; - -import com.netflix.config.DynamicPropertyFactory; - -public class ServiceRegistryCommonConfig { - private static final String REGISTRY_EMPTY_PROTECTION = "servicecomb.service.registry.instance.empty.protection"; - - private static final String REGISTRY_FILTER_UP_INSTANCES = "servicecomb.service.registry.instance.useUpInstancesOnly"; - - public static boolean isEmptyInstanceProtectionEnabled() { - return - DynamicPropertyFactory.getInstance() - .getBooleanProperty(REGISTRY_EMPTY_PROTECTION, - true).get(); - } - - public static boolean useUpInstancesOnly() { - return - DynamicPropertyFactory.getInstance() - .getBooleanProperty(REGISTRY_FILTER_UP_INSTANCES, - false).get(); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/AppManager.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/AppManager.java deleted file mode 100644 index f149516e92e..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/AppManager.java +++ /dev/null @@ -1,101 +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.servicecomb.registry.consumer; - -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; - -import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; -import org.apache.servicecomb.foundation.common.event.EventManager; -import org.apache.servicecomb.registry.api.event.MicroserviceInstanceChangedEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.eventbus.EventBus; - -public class AppManager { - private static final Logger LOGGER = LoggerFactory.getLogger(AppManager.class); - - // key: appId - private final Map apps = new ConcurrentHashMapEx<>(); - - public AppManager() { - getEventBus().register(this); - } - - public EventBus getEventBus() { - return EventManager.getEventBus(); - } - - public Map getApps() { - return apps; - } - - // microserviceName maybe normal name or alias name - public MicroserviceVersionRule getOrCreateMicroserviceVersionRule(String appId, String microserviceName, - String versionRule) { - MicroserviceManager microserviceManager = getOrCreateMicroserviceManager(appId); - - return microserviceManager.getOrCreateMicroserviceVersionRule(microserviceName, versionRule); - } - - public MicroserviceManager getOrCreateMicroserviceManager(String appId) { - return apps.computeIfAbsent(appId, id -> new MicroserviceManager(this, appId)); - } - - public CompletableFuture getOrCreateMicroserviceVersionsAsync(String appId - , String microserviceName) { - MicroserviceManager microserviceManager = getOrCreateMicroserviceManager(appId); - return microserviceManager.getOrCreateMicroserviceVersionsAsync(microserviceName); - } - - public MicroserviceVersions getOrCreateMicroserviceVersions(String appId, String microserviceName) { - MicroserviceManager microserviceManager = getOrCreateMicroserviceManager(appId); - return microserviceManager.getOrCreateMicroserviceVersions(microserviceName); - } - - public void onMicroserviceInstanceChanged(MicroserviceInstanceChangedEvent changedEvent) { - MicroserviceManager microserviceManager = apps.get(changedEvent.getKey().getAppId()); - if (microserviceManager == null) { - return; - } - - microserviceManager.onMicroserviceInstanceChanged(changedEvent); - } - - public void pullInstances() { - for (MicroserviceManager microserviceManager : apps.values()) { - microserviceManager.pullInstances(); - } - } - - public void safePullInstances() { - try { - pullInstances(); - } catch (Exception e) { - LOGGER.error("failed to pull instances.", e); - } - } - - public void markWaitingDelete(String appId, String microserviceName) { - Optional.ofNullable(apps.get(appId)) - .map(microserviceManager -> microserviceManager.getVersionsByName().get(microserviceName)) - .ifPresent(MicroserviceVersions::markWaitingDelete); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceInstancePing.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceInstancePing.java deleted file mode 100644 index 0f4e1fc9b3b..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceInstancePing.java +++ /dev/null @@ -1,34 +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.servicecomb.registry.consumer; - -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; - -/** - * SPI interface to ping microservice instance status in Instance Protection Mode, which means - * protection of instance removal and usually used in scenarios where instances are fixed and changed rarely. - */ -public interface MicroserviceInstancePing { - int getOrder(); - - /** - * check if this instance if valid to use - * @param instance - * @return - */ - boolean ping(MicroserviceInstance instance); -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceManager.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceManager.java deleted file mode 100644 index e873a94dbcc..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceManager.java +++ /dev/null @@ -1,140 +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.servicecomb.registry.consumer; - -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; -import org.apache.servicecomb.foundation.vertx.executor.SinglePoolBlockingExecutor; -import org.apache.servicecomb.registry.api.event.MicroserviceInstanceChangedEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.vertx.core.Vertx; - -public class MicroserviceManager { - private static final Logger LOGGER = LoggerFactory.getLogger(MicroserviceManager.class); - - private final AppManager appManager; - - private final String appId; - - // key: microserviceName - private final Map versionsByName = new ConcurrentHashMapEx<>(); - - private final Object lock = new Object(); - - public MicroserviceManager(AppManager appManager, String appId) { - this.appManager = appManager; - this.appId = appId; - } - - public Map getVersionsByName() { - return versionsByName; - } - - /** - * update instance information triggered by first timeout pull - */ - public MicroserviceVersions getOrCreateMicroserviceVersions(String microserviceName) { - // do not use ConcurrentHashMap computeIfAbsent for versionsByName - // because: when create MicroserviceVersions, one creation may depend on another - // MicroserviceVersions. And pullInstances will create a new MicroserviceVersions. - // Calling ConcurrentHashMap computeIfAbsent inside will get deadlock. - MicroserviceVersions microserviceVersions = versionsByName.get(microserviceName); - if (microserviceVersions == null) { - synchronized (lock) { - microserviceVersions = versionsByName.get(microserviceName); - if (microserviceVersions == null) { - microserviceVersions = new MicroserviceVersions(appManager, appId, microserviceName); - microserviceVersions.pullInstances(); - versionsByName.put(microserviceName, microserviceVersions); - } - } - } - - tryRemoveInvalidMicroservice(microserviceVersions); - - return microserviceVersions; - } - - public CompletableFuture getOrCreateMicroserviceVersionsAsync(String microserviceName) { - MicroserviceVersions microserviceVersions = versionsByName.get(microserviceName); - if (microserviceVersions == null) { - if (Vertx.currentContext() == null) { - // not in event-loop, execute in current thread - return CompletableFuture.completedFuture(getOrCreateMicroserviceVersions(microserviceName)); - } else { - // execute in an single thread pool to make sure make less requests to service center - return CompletableFuture.supplyAsync(() -> getOrCreateMicroserviceVersions(microserviceName), - SinglePoolBlockingExecutor.create()); - } - } else { - // here do not need switch to another thread, can improve performance - tryRemoveInvalidMicroservice(microserviceVersions); - return CompletableFuture.completedFuture(microserviceVersions); - } - } - - private void tryRemoveInvalidMicroservice(MicroserviceVersions microserviceVersions) { - if (!microserviceVersions.isWaitingDelete()) { - return; - } - - // remove this microservice if it does not exist or not registered in order to get it back when access it again - String microserviceName = microserviceVersions.getMicroserviceName(); - if (versionsByName.remove(microserviceName) != null) { - microserviceVersions.destroy(); - LOGGER.info("remove microservice, appId={}, microserviceName={}.", appId, microserviceName); - } - } - - public MicroserviceVersionRule getOrCreateMicroserviceVersionRule(String microserviceName, - String versionRule) { - MicroserviceVersions microserviceVersions = getOrCreateMicroserviceVersions(microserviceName); - - return microserviceVersions.getOrCreateMicroserviceVersionRule(versionRule); - } - - /** - * update instance information triggered by scheduler - */ - public void pullInstances() { - synchronized (lock) { - for (MicroserviceVersions microserviceVersions : versionsByName.values()) { - microserviceVersions.pullInstances(); - - tryRemoveInvalidMicroservice(microserviceVersions); - } - } - } - - /** - * update instance information triggered by event - */ - public void onMicroserviceInstanceChanged(MicroserviceInstanceChangedEvent changedEvent) { - synchronized (lock) { - for (MicroserviceVersions microserviceVersions : versionsByName.values()) { - microserviceVersions.onMicroserviceInstanceChanged(changedEvent); - - tryRemoveInvalidMicroservice(microserviceVersions); - } - } - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceVersion.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceVersion.java deleted file mode 100644 index f98761ee403..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceVersion.java +++ /dev/null @@ -1,114 +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.servicecomb.registry.consumer; - -import java.util.Collection; -import java.util.List; - -import org.apache.servicecomb.foundation.common.VendorExtensions; -import org.apache.servicecomb.registry.api.event.CreateMicroserviceVersionEvent; -import org.apache.servicecomb.registry.api.event.DestroyMicroserviceVersionEvent; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.foundation.common.Version; - -public class MicroserviceVersion { - protected AppManager appManager; - - protected MicroserviceVersions microserviceVersions; - - // because of cross app invoke - // microserviceName not always equals microservice.serviceName - protected String microserviceName; - - protected Version version; - - protected Microservice microservice; - - protected Collection instances; - - private final VendorExtensions vendorExtensions = new VendorExtensions(); - - public MicroserviceVersion(MicroserviceVersions microserviceVersions, String microserviceId, - String microserviceName, - Collection instances) { - Microservice microservice = DiscoveryManager.INSTANCE.getMicroservice(microserviceId); - if (microservice == null) { - throw new IllegalStateException( - String.format("failed to query by microserviceId '%s' from ServiceCenter.", microserviceId)); - } - - init(microserviceVersions, microservice, microserviceName, instances); - appManager.getEventBus().post(new CreateMicroserviceVersionEvent(this)); - } - - public MicroserviceVersion(MicroserviceVersions microserviceVersions, - Microservice microservice, String microserviceName, - Collection instances) { - init(microserviceVersions, microservice, microserviceName, instances); - appManager.getEventBus().post(new CreateMicroserviceVersionEvent(this)); - } - - protected void init(MicroserviceVersions microserviceVersions, Microservice microservice, - String microserviceName, - Collection instances) { - this.appManager = microserviceVersions.getAppManager(); - this.microserviceVersions = microserviceVersions; - this.microservice = microservice; - this.microserviceName = microserviceName; - this.instances = instances; - this.version = new Version(microservice.getVersion()); - } - - public MicroserviceVersions getMicroserviceVersions() { - return microserviceVersions; - } - - public Collection getInstances() { - return instances; - } - - public void setInstances(List instances) { - this.instances = instances; - } - - public String getMicroserviceName() { - return microserviceName; - } - - public String getMicroserviceId() { - return microservice.getServiceId(); - } - - public Microservice getMicroservice() { - return microservice; - } - - public Version getVersion() { - return version; - } - - public VendorExtensions getVendorExtensions() { - return vendorExtensions; - } - - public void destroy() { - appManager.getEventBus().post(new DestroyMicroserviceVersionEvent(this)); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceVersionRule.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceVersionRule.java deleted file mode 100644 index d123ffe07ef..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceVersionRule.java +++ /dev/null @@ -1,233 +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.servicecomb.registry.consumer; - -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.apache.servicecomb.foundation.common.cache.VersionedCache; -import org.apache.servicecomb.foundation.common.utils.StringBuilderUtils; -import org.apache.servicecomb.registry.api.registry.Framework; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.cache.InstanceCache; -import org.apache.servicecomb.registry.definition.DefinitionConst; -import org.apache.servicecomb.registry.version.VersionRule; -import org.apache.servicecomb.registry.version.VersionRuleUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MicroserviceVersionRule { - private static final Logger LOGGER = LoggerFactory.getLogger(MicroserviceVersionRule.class); - - private final String appId; - - private final String microserviceName; - - private final VersionRule versionRule; - - // wrap variable data to make them atomic - private MicroserviceVersionRuleData data; - - public MicroserviceVersionRule(String appId, String microserviceName, String strVersionRule) { - this.appId = appId; - this.microserviceName = microserviceName; - this.versionRule = VersionRuleUtils.getOrCreate(strVersionRule); - - data = createEmptyData(); - } - - public String getAppId() { - return appId; - } - - public String getMicroserviceName() { - return microserviceName; - } - - public VersionRule getVersionRule() { - return versionRule; - } - - public T getLatestMicroserviceVersion() { - return data.getLatestMicroserviceVersion(); - } - - public Map getInstances() { - return data.instances; - } - - public InstanceCache getInstanceCache() { - return data.instanceCache; - } - - public VersionedCache getVersionedCache() { - return data.versionedCache; - } - - public MicroserviceVersionRuleData getData() { - return data; - } - - /** - * - * @param allVersions all versions even not belongs to this rule - * @param allInstances all instances even not belongs to this rule, related MicroserviceVersion always exists - */ - public void update(Map allVersions, Collection allInstances) { - if (allInstances == null) { - allInstances = Collections.emptyList(); - } - - if (DefinitionConst.VERSION_RULE_LATEST.equals(versionRule.getVersionRule())) { - data = createDataByLatestRule(allVersions, allInstances); - } else { - data = createDataByOtherRule(allVersions, allInstances); - } - - printData(data, allVersions.size(), allInstances.size()); - } - - private void printData(MicroserviceVersionRuleData data, int inputVersionCount, int inputInstanceCount) { - String latestVersion = data.latestVersion == null ? null : data.latestVersion.getVersion().getVersion(); - if (data.getInstances().isEmpty()) { - LOGGER.info( - "update instances to be empty caused by version rule, appId={}, microserviceName={}, versionRule={}" - + ", latestVersion={}, inputVersionCount={}, inputInstanceCount={}", - appId, microserviceName, versionRule.getVersionRule(), - latestVersion, inputVersionCount, inputInstanceCount); - return; - } - - StringBuilder sb = new StringBuilder(); - StringBuilderUtils - .appendLine(sb, - "update instances, appId=%s, microserviceName=%s, versionRule=%s" - + ", latestVersion=%s, inputVersionCount=%s, inputInstanceCount=%s", - appId, microserviceName, versionRule.getVersionRule(), - latestVersion, inputVersionCount, inputInstanceCount); - - int idx = 0; - for (MicroserviceInstance instance : data.getInstances().values()) { - MicroserviceVersion microserviceVersion = data.versions.get(instance.getServiceId()); - Microservice microservice = microserviceVersion.getMicroservice(); - Framework framework = microservice.getFramework(); - String frameworkName = framework == null ? "unknown" : framework.getName(); - String frameworkVersion = framework == null ? "unknown" : framework.getVersion(); - - StringBuilderUtils.appendLine(sb, - " %d.instanceId=%s, status=%s, version=%s, endpoints=%s, environment=%s, framework.name=%s, framework.version=%s", - idx, - instance.getInstanceId(), instance.getStatus(), microserviceVersion.getVersion(), instance.getEndpoints(), - microservice.getEnvironment(), frameworkName, frameworkVersion); - idx++; - } - - LOGGER.info(StringBuilderUtils.deleteLast(sb, 1).toString()); - } - - private void initData(MicroserviceVersionRuleData data) { - if (data.latestVersion == null) { - data.latestVersion = findLatest(data.versions, data.instances.values()); - } - - data.instanceCache = new InstanceCache(appId, microserviceName, versionRule.getVersionRule(), data.instances); - data.versionedCache = new VersionedCache() - .name(versionRule.getVersionRule()) - .autoCacheVersion() - .data(data.instances); - } - - private MicroserviceVersionRuleData createEmptyData() { - MicroserviceVersionRuleData data = new MicroserviceVersionRuleData(); - data.versions = Collections.emptyMap(); - data.instances = Collections.emptyMap(); - initData(data); - return data; - } - - private MicroserviceVersion findLatest(Map allVersions, - Collection allInstances) { - if (allInstances.isEmpty()) { - return allVersions.isEmpty() ? null : allVersions.values().stream() - .max(Comparator.comparing(MicroserviceVersion::getVersion)) - .get(); - } - - MicroserviceVersion latestVersion = null; - for (MicroserviceInstance instance : allInstances) { - // never to be null - MicroserviceVersion version = allVersions.get(instance.getServiceId()); - if (latestVersion == null || latestVersion.version.compareTo(version.version) < 0) { - latestVersion = version; - } - } - return latestVersion; - } - - private MicroserviceVersionRuleData createDataByLatestRule(Map allVersions, - Collection allInstances) { - // find latest - MicroserviceVersion latestVersion = findLatest(allVersions, allInstances); - if (latestVersion == null) { - return createEmptyData(); - } - - MicroserviceVersionRuleData data = new MicroserviceVersionRuleData(); - - String serviceId = latestVersion.getMicroservice().getServiceId(); - data.latestVersion = latestVersion; - data.versions = Collections.singletonMap(serviceId, latestVersion); - data.instances = allInstances.stream() - .filter(instance -> instance.getServiceId().equals(serviceId)) - .collect(Collectors.toMap(MicroserviceInstance::getInstanceId, Function.identity())); - data.instances = Collections.unmodifiableMap(data.instances); - - initData(data); - - return data; - } - - private MicroserviceVersionRuleData createDataByOtherRule(Map allVersions, - Collection allInstances) { - MicroserviceVersionRuleData data = new MicroserviceVersionRuleData(); - - // filter versions - data.versions = allVersions.values().stream() - .filter(v -> versionRule.isAccept(v.version)) - .collect(Collectors.toMap(MicroserviceVersion::getMicroserviceId, Function.identity())); - data.versions = Collections.unmodifiableMap(data.versions); - if (data.versions.isEmpty()) { - return createEmptyData(); - } - - // filter instances - data.instances = allInstances.stream() - .filter(instance -> data.versions.containsKey(instance.getServiceId())) - .collect(Collectors.toMap(MicroserviceInstance::getInstanceId, Function.identity())); - data.instances = Collections.unmodifiableMap(data.instances); - - initData(data); - - return data; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceVersionRuleData.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceVersionRuleData.java deleted file mode 100644 index 04371bce9fb..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceVersionRuleData.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.servicecomb.registry.consumer; - -import java.util.Map; - -import org.apache.servicecomb.foundation.common.cache.VersionedCache; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.cache.InstanceCache; - -public class MicroserviceVersionRuleData { - // if instances is not empty, latestVersion is relate to instances - // if instances is empty, latestVersion is relate to versions - MicroserviceVersion latestVersion; - - // key is microserviceId - Map versions; - - // key is instanceId - Map instances; - - InstanceCache instanceCache; - - VersionedCache versionedCache; - - @SuppressWarnings("unchecked") - public T getLatestMicroserviceVersion() { - return (T) latestVersion; - } - - public Map getVersions() { - return versions; - } - - public Map getInstances() { - return instances; - } - - public InstanceCache getInstanceCache() { - return instanceCache; - } - - public VersionedCache getVersionedCache() { - return versionedCache; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceVersions.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceVersions.java deleted file mode 100644 index 563f872b6d8..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/MicroserviceVersions.java +++ /dev/null @@ -1,335 +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.servicecomb.registry.consumer; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.stream.Collectors; - -import org.apache.servicecomb.foundation.common.VendorExtensions; -import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.api.event.CreateMicroserviceEvent; -import org.apache.servicecomb.registry.api.event.DestroyMicroserviceEvent; -import org.apache.servicecomb.registry.api.event.MicroserviceInstanceChangedEvent; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstanceStatus; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstances; -import org.apache.servicecomb.registry.config.ServiceRegistryCommonConfig; -import org.apache.servicecomb.registry.definition.DefinitionConst; -import org.apache.servicecomb.registry.definition.MicroserviceNameParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.annotations.VisibleForTesting; - -public class MicroserviceVersions { - private static final Logger LOGGER = LoggerFactory.getLogger(MicroserviceVersions.class); - - protected AppManager appManager; - - protected String appId; - - protected String shortName; - - protected String microserviceName; - - // revision and pulledInstances directly equals to SC's response - protected String revision = null; - - private List pulledInstances; - - private MicroserviceInstances lastPulledResult; - - // instances not always equals to pulledInstances - // in the future: - // pulledInstances means all instance - // instances means available instance - Collection instances; - - // key is service id - Map versions = new ConcurrentHashMapEx<>(); - - // key is version rule - Map versionRules = new ConcurrentHashMapEx<>(); - - // process pulled instances and create versionRule must be protected by lock - // otherwise maybe lost instance or version in versionRule - private final Object lock = new Object(); - - private long lastPullTime = 0; - - private boolean waitingDelete = false; - - private final VendorExtensions vendorExtensions = new VendorExtensions(); - - public MicroserviceVersions(AppManager appManager, String appId, String microserviceName) { - this.appManager = appManager; - this.appId = appId; - this.microserviceName = microserviceName; - this.shortName = new MicroserviceNameParser(appId, microserviceName).getShortName(); - - appManager.getEventBus().post(new CreateMicroserviceEvent(this)); - - LOGGER.info("create MicroserviceVersions, appId={}, microserviceName={}.", - appId, - microserviceName); - } - - public boolean isWaitingDelete() { - return waitingDelete; - } - - public MicroserviceVersions markWaitingDelete() { - this.waitingDelete = true; - return this; - } - - public AppManager getAppManager() { - return appManager; - } - - public String getAppId() { - return appId; - } - - public String getMicroserviceName() { - return microserviceName; - } - - public String getShortName() { - return shortName; - } - - @VisibleForTesting - public Map getVersions() { - synchronized (lock) { - return versions; - } - } - - @SuppressWarnings("unchecked") - public T getVersion(String serviceId) { - synchronized (lock) { - return (T) versions.get(serviceId); - } - } - - public String getRevision() { - return revision; - } - - public void setRevision(String revision) { - this.revision = revision; - } - - public List getPulledInstances() { - return pulledInstances; - } - - public long getLastPullTime() { - return lastPullTime; - } - - public MicroserviceInstances getLastPulledResult() { - return lastPulledResult; - } - - public VendorExtensions getVendorExtensions() { - return vendorExtensions; - } - - public void pullInstances() { - lastPullTime = System.currentTimeMillis(); - MicroserviceInstances microserviceInstances = findServiceInstances(); - lastPulledResult = microserviceInstances; - if (microserviceInstances == null) { - // pulled failed, did not get anything - // will not do anything, consumers will use existing instances - return; - } - if (microserviceInstances.isMicroserviceNotExist()) { - // pulled failed, SC said target not exist - waitingDelete = true; - return; - } - - if (null != revision && revision.equals(microserviceInstances.getRevision())) { - return; - } - - pulledInstances = microserviceInstances.getInstancesResponse().getInstances(); - pulledInstances.sort(Comparator.comparing(MicroserviceInstance::getInstanceId)); - String rev = microserviceInstances.getRevision(); - - safeSetInstances(pulledInstances, rev); - } - - protected MicroserviceInstances findServiceInstances() { - return DiscoveryManager.INSTANCE.findServiceInstances(appId, - microserviceName, - DefinitionConst.VERSION_RULE_ALL, - revision); - } - - protected void safeSetInstances(List pulledInstances, String rev) { - try { - List filteredInstance = pulledInstances; - // 增加一个配置项只使用 `UP` 实例。 在使用 `TESTING` 进行拨测, 并且配置了 - // servicecomb.references.version-rule=latest 场景,需要保证不使用 - // `TESTING` 实例。 不能依赖 InstanceStatusDiscoveryFilter, 避免 - // 构建的 VersionRule 实例列表为空。 - if (ServiceRegistryCommonConfig.useUpInstancesOnly()) { - filteredInstance = pulledInstances.stream().filter(item -> MicroserviceInstanceStatus.UP == item.getStatus()) - .collect(Collectors.toList()); - } - setInstances(filteredInstance, rev); - } catch (Throwable e) { - waitingDelete = true; - LOGGER.error("Failed to setInstances, appId={}, microserviceName={}.", - getAppId(), - getMicroserviceName(), - e); - } - } - - static class MergedInstances { - // key is microserviceId - Map> microserviceIdMap = new HashMap<>(); - - // key is instanceId - Map instanceIdMap = new HashMap<>(); - - void addInstance(MicroserviceInstance instance) { - instanceIdMap.put(instance.getInstanceId(), instance); - microserviceIdMap - .computeIfAbsent(instance.getServiceId(), key -> new ArrayList<>()) - .add(instance); - } - } - - private void setInstances(List pulledInstances, String rev) { - synchronized (lock) { - MergedInstances mergedInstances = mergeInstances(pulledInstances, instances); - instances = mergedInstances.instanceIdMap.values(); - // clear cache - versions.forEach((key, value) -> value.setInstances(new ArrayList<>())); - for (Entry> entry : mergedInstances.microserviceIdMap.entrySet()) { - // always update microservice versions, because we allow microservice info override, like schema info - MicroserviceVersion newVersion = createMicroserviceVersion(entry.getKey(), entry.getValue()); - newVersion.setInstances(entry.getValue()); - versions.put(entry.getKey(), newVersion); - } - - for (MicroserviceVersionRule microserviceVersionRule : versionRules.values()) { - microserviceVersionRule.update(versions, instances); - } - revision = rev; - } - } - - protected MicroserviceVersion createMicroserviceVersion(String microserviceId, List instances) { - return new MicroserviceVersion(this, microserviceId, microserviceName, instances); - } - - private MergedInstances mergeInstances(List pulledInstances, - Collection inUseInstances) { - MergedInstances mergedInstances = new MergedInstances(); - pulledInstances.stream().forEach(mergedInstances::addInstance); - MicroserviceInstancePing ping = SPIServiceUtils.getPriorityHighestService(MicroserviceInstancePing.class); - - if (pulledInstances.isEmpty() && inUseInstances != null && ServiceRegistryCommonConfig - .isEmptyInstanceProtectionEnabled()) { - - inUseInstances.stream().forEach(instance -> { - if (!mergedInstances.instanceIdMap.containsKey(instance.getInstanceId())) { - if (ping.ping(instance)) { - mergedInstances.addInstance(instance); - } - } - }); - } - - return mergedInstances; - } - - public MicroserviceVersionRule getOrCreateMicroserviceVersionRule(String versionRule) { - // do not use computeIfAbsent - MicroserviceVersionRule microserviceVersionRule = versionRules.get(versionRule); - if (microserviceVersionRule == null) { - synchronized (lock) { - microserviceVersionRule = versionRules.computeIfAbsent(versionRule, this::createAndInitMicroserviceVersionRule); - } - } - - return microserviceVersionRule; - } - - protected MicroserviceVersionRule createAndInitMicroserviceVersionRule(String strVersionRule) { - LOGGER.info("create MicroserviceVersionRule, appId={}, microserviceName={}, versionRule={}.", - appId, - microserviceName, - strVersionRule); - - MicroserviceVersionRule microserviceVersionRule = - new MicroserviceVersionRule(appId, microserviceName, strVersionRule); - microserviceVersionRule.update(versions, instances); - return microserviceVersionRule; - } - - public void onMicroserviceInstanceChanged(MicroserviceInstanceChangedEvent changedEvent) { - if (!isEventAccept(changedEvent)) { - return; - } - // pull instances always replace old instances, not append - // - // pull result and watch event sequence is not defined even inside SC. - // it's not safe to trust the event, so we just send a new pull request - // - // CREATE/UPDATE: - // if pull 1/2/3, and then add 4, but "add 4" received before pull result, will lost 4. - // DELETE: - // if pull 1/2/3, and then delete 3, but "delete 3" received before pull result, will have wrong 3. - // EXPIRE:: - // black/white config in SC changed, we must refresh all data from sc. - pullInstances(); - } - - protected boolean isEventAccept(MicroserviceInstanceChangedEvent changedEvent) { - return (appId.equals(changedEvent.getKey().getAppId()) && - microserviceName.equals(changedEvent.getKey().getServiceName())) || - microserviceName.equals( - changedEvent.getKey().getAppId() + DefinitionConst.APP_SERVICE_SEPARATOR + changedEvent.getKey() - .getServiceName()); - } - - public void destroy() { - synchronized (lock) { - for (MicroserviceVersion microserviceVersion : versions.values()) { - microserviceVersion.destroy(); - } - } - appManager.getEventBus().post(new DestroyMicroserviceEvent(this)); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/SimpleMicroserviceInstancePing.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/SimpleMicroserviceInstancePing.java deleted file mode 100644 index e9c204c5c09..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/SimpleMicroserviceInstancePing.java +++ /dev/null @@ -1,56 +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.servicecomb.registry.consumer; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; - -import org.apache.servicecomb.foundation.common.net.IpPort; -import org.apache.servicecomb.foundation.common.net.NetUtils; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.CollectionUtils; - -/** - * Simple implementation of .MicroserviceInstancePing using telnet - */ -public class SimpleMicroserviceInstancePing implements MicroserviceInstancePing { - private static final Logger LOGGER = LoggerFactory.getLogger(SimpleMicroserviceInstancePing.class); - - @Override - public int getOrder() { - return 100; - } - - @Override - public boolean ping(MicroserviceInstance instance) { - if (!CollectionUtils.isEmpty(instance.getEndpoints())) { - for (String endpoint : instance.getEndpoints()) { - IpPort ipPort = NetUtils.parseIpPortFromURI(endpoint); - try (Socket s = new Socket()) { - s.connect(new InetSocketAddress(ipPort.getHostOrIp(), ipPort.getPort()), 3000); - return true; - } catch (IOException e) { - LOGGER.warn("ping instance {} endpoint {} failed", instance.getInstanceId(), endpoint); - } - } - } - return false; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/StaticMicroserviceVersions.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/StaticMicroserviceVersions.java deleted file mode 100644 index dae01c73cce..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/consumer/StaticMicroserviceVersions.java +++ /dev/null @@ -1,111 +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.servicecomb.registry.consumer; - -import java.util.List; -import java.util.Map; - -import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.registry.api.registry.FindInstancesResponse; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstances; -import org.apache.servicecomb.foundation.common.Version; -import org.apache.servicecomb.swagger.SwaggerUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.ImmutableMap; - -import io.swagger.models.Swagger; - -public class StaticMicroserviceVersions extends MicroserviceVersions { - private static final Logger LOGGER = LoggerFactory.getLogger(StaticMicroserviceVersions.class); - - protected final Microservice microservice = new Microservice(); - - protected final MicroserviceInstances microserviceInstances = new MicroserviceInstances(); - - public StaticMicroserviceVersions(AppManager appManager, String appId, String microserviceName) { - super(appManager, appId, microserviceName); - } - - public StaticMicroserviceVersions init(Class schemaIntfCls, String version, - List instances) { - return init(ImmutableMap.of(microserviceName, schemaIntfCls), version, instances); - } - - public StaticMicroserviceVersions init(Map> schemaByIdMap, String version, - List instances) { - createMicroservice(version); - addSchemas(schemaByIdMap); - addInstances(instances); - pullInstances(); - - return this; - } - - protected void createMicroservice(String version) { - String environment = RegistrationManager.INSTANCE.getMicroservice().getEnvironment(); - - microservice.setAppId(this.getAppId()); - microservice.setServiceName(this.getShortName()); - microservice.setVersion(new Version(version).getVersion()); - microservice.setServiceId(this.getAppId() + "-" - + environment + "-" - + this.getMicroserviceName() + "-" - + microservice.getVersion()); - microservice.setEnvironment(environment); - } - - protected void addSchemas(Map> schemaByIdMap) { - schemaByIdMap.forEach(this::addSchema); - } - - protected void addSchema(String schemaId, Class schemaClass) { - Swagger swagger = RegistrationManager.INSTANCE.getSwaggerLoader() - .registerSwagger(appId, shortName, schemaId, schemaClass); - String swaggerContent = SwaggerUtils.swaggerToString(swagger); - LOGGER.debug("generate swagger for 3rd party service [{}], swagger: {}", microserviceName, swaggerContent); - microservice.addSchema(schemaId, swaggerContent); - } - - protected void addInstances(List instances) { - for (int idx = 0; idx < instances.size(); idx++) { - MicroserviceInstance instance = instances.get(idx); - instance.setServiceId(microservice.getServiceId()); - instance.setInstanceId(microservice.getServiceId() + "-" + idx); - } - microserviceInstances.setMicroserviceNotExist(false); - microserviceInstances.setInstancesResponse(new FindInstancesResponse()); - microserviceInstances.getInstancesResponse().setInstances(instances); - } - - @Override - protected MicroserviceInstances findServiceInstances() { - // Only refreshed for the first time - microserviceInstances.setNeedRefresh(revision == null); - microserviceInstances.setRevision("1"); - return microserviceInstances; - } - - @Override - protected MicroserviceVersion createMicroserviceVersion(String microserviceId, List instances) { - return new MicroserviceVersion(this, microservice, microserviceName, instances); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/definition/DefinitionConst.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/definition/DefinitionConst.java index 898bc3a86f4..528caaafde9 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/definition/DefinitionConst.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/definition/DefinitionConst.java @@ -33,15 +33,5 @@ public interface DefinitionConst { String INSTANCE_PUBKEY_PRO = "publickey"; - String REGISTER_URL_PREFIX = "servicecomb.service.registry.registerUrlPrefix"; - - String REGISTER_SERVICE_PATH = "servicecomb.service.registry.registerPath"; - - String REGISTRY_APP_ID = "default"; - - String REGISTRY_SERVICE_NAME = "SERVICECENTER"; - - String SWAGGER_EXPORT_ENABLED = "servicecomb.swagger.export.enabled"; - - String SWAGGER_DIRECTORY = "servicecomb.swagger.export.directory"; + String REGISTER_URL_PREFIX = "servicecomb.registry.registerUrlPrefix"; } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/definition/MicroserviceNameParser.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/definition/MicroserviceNameParser.java index be1c92faaea..e99afdbf8be 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/definition/MicroserviceNameParser.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/definition/MicroserviceNameParser.java @@ -17,25 +17,14 @@ package org.apache.servicecomb.registry.definition; /** - *
- *   1. if microserviceName format is app:name, then will ignore defaultAppId
- *     appId = app
- *     shortName = name
- *     microserviceName = app:name
- *   2. if microserviceName format is name
- *     appId = defaultAppId
- *     shortName = name
- *     microserviceName = name
- * 
+ * Parse application and microservice from join name. + * + * 1. microserviceName + * 2. application:microserviceName */ public class MicroserviceNameParser { private String appId; - // always not include appId - private String shortName; - - // inside app: equals to shortName - // cross app: appId:shortName private String microserviceName; public MicroserviceNameParser(String defaultAppId, String microserviceName) { @@ -43,27 +32,21 @@ public MicroserviceNameParser(String defaultAppId, String microserviceName) { } private void parseMicroserviceName(String defaultAppId, String microserviceName) { - this.microserviceName = microserviceName; - int idxAt = microserviceName.indexOf(DefinitionConst.APP_SERVICE_SEPARATOR); if (idxAt == -1) { this.appId = defaultAppId; - this.shortName = microserviceName; + this.microserviceName = microserviceName; return; } this.appId = microserviceName.substring(0, idxAt); - this.shortName = microserviceName.substring(idxAt + 1); + this.microserviceName = microserviceName.substring(idxAt + 1); } public String getAppId() { return appId; } - public String getShortName() { - return shortName; - } - public String getMicroserviceName() { return microserviceName; } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractDiscoveryFilter.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractDiscoveryFilter.java index 36e88770ee6..f274799c30e 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractDiscoveryFilter.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractDiscoveryFilter.java @@ -17,14 +17,25 @@ package org.apache.servicecomb.registry.discovery; -import java.util.HashMap; +import java.util.ArrayList; +import org.apache.servicecomb.config.DynamicProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; public abstract class AbstractDiscoveryFilter implements DiscoveryFilter { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDiscoveryFilter.class); + protected Boolean enabled; + + protected DynamicProperties dynamicProperties; + + @Autowired + public void setDynamicProperties(DynamicProperties dynamicProperties) { + this.dynamicProperties = dynamicProperties; + } + @Override public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { if (!parent.childrenInited()) { @@ -39,8 +50,8 @@ public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode p String childName = findChildName(context, parent); DiscoveryTreeNode node = parent.child(childName); if (node == null) { - LOGGER.warn("discovery filter {} return null.", this.getClass().getName()); - return new DiscoveryTreeNode().subName(parent, "empty").data(new HashMap<>()); + LOGGER.warn("discovery filter {}/{} return null.", this.getClass().getSimpleName(), childName); + return new DiscoveryTreeNode().subName(parent, "empty").data(new ArrayList<>()); } return node; } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractEndpointDiscoveryFilter.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractEndpointDiscoveryFilter.java index 1aa2d61558b..f4b464f0097 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractEndpointDiscoveryFilter.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractEndpointDiscoveryFilter.java @@ -17,12 +17,10 @@ package org.apache.servicecomb.registry.discovery; -import java.net.URI; import java.util.ArrayList; import java.util.List; -import java.util.Map; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,6 +29,10 @@ public abstract class AbstractEndpointDiscoveryFilter implements DiscoveryFilter private static final String ALL_TRANSPORT = ""; + private static final String WEBSOCKET_TRANSPORT = "websocket"; + + private static final String REST_TRANSPORT = "rest"; + @Override public boolean isGroupingFilter() { return true; @@ -43,20 +45,23 @@ public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode p .computeIfAbsent(expectTransportName, etn -> createDiscoveryTreeNode(expectTransportName, context, parent)); } - @SuppressWarnings("unchecked") protected DiscoveryTreeNode createDiscoveryTreeNode(String expectTransportName, DiscoveryContext context, DiscoveryTreeNode parent) { List endpoints = new ArrayList<>(); - for (MicroserviceInstance instance : ((Map) parent.data()).values()) { + List instances = parent.data(); + for (StatefulDiscoveryInstance instance : instances) { for (String endpoint : instance.getEndpoints()) { try { - URI uri = URI.create(endpoint); - String transportName = uri.getScheme(); - if (!isTransportNameMatch(transportName, expectTransportName)) { + URIEndpointObject endpointObject = new URIEndpointObject(endpoint); + String transPortName = endpointObject.getSchema(); + if (endpointObject.isWebsocketEnabled() && WEBSOCKET_TRANSPORT.equals(expectTransportName)) { + transPortName = WEBSOCKET_TRANSPORT; + } + if (!isTransportNameMatch(transPortName, expectTransportName)) { continue; } - Object objEndpoint = createEndpoint(context, transportName, endpoint, instance); + Object objEndpoint = createEndpoint(context, transPortName, endpoint, instance); if (objEndpoint == null) { continue; } @@ -74,11 +79,14 @@ protected DiscoveryTreeNode createDiscoveryTreeNode(String expectTransportName, } protected boolean isTransportNameMatch(String transportName, String expectTransportName) { - return ALL_TRANSPORT.equals(expectTransportName) || transportName.equals(expectTransportName); + if (ALL_TRANSPORT.equals(expectTransportName)) { + return true; + } + return transportName.equals(expectTransportName); } protected abstract String findTransportName(DiscoveryContext context, DiscoveryTreeNode parent); protected abstract Object createEndpoint(DiscoveryContext context, String transportName, String endpoint, - MicroserviceInstance instance); + StatefulDiscoveryInstance instance); } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractGroupDiscoveryFilter.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractGroupDiscoveryFilter.java new file mode 100644 index 00000000000..b76d860903d --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractGroupDiscoveryFilter.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.servicecomb.registry.discovery; + +public abstract class AbstractGroupDiscoveryFilter extends AbstractDiscoveryFilter { + @Override + public boolean isGroupingFilter() { + return true; + } + + protected abstract String groupsSizeParameter(); + + protected abstract String contextParameter(); + + protected abstract String groupPrefix(); + + @Override + protected String findChildName(DiscoveryContext context, DiscoveryTreeNode parent) { + Integer level = context.getContextParameter(contextParameter()); + Integer groups = parent.attribute(groupsSizeParameter()); + + String group; + if (level == null) { + group = groupPrefix() + 1; + if (groups > 1) { + context.pushRerunFilter(); + context.putContextParameter(contextParameter(), 1); + } + return group; + } + + level = level + 1; + group = groupPrefix() + level; + + if (level < groups) { + context.pushRerunFilter(); + context.putContextParameter(contextParameter(), level); + } + return group; + } +} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryFilter.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryFilter.java index e0b395cd845..14b0cc44681 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryFilter.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryFilter.java @@ -17,16 +17,23 @@ package org.apache.servicecomb.registry.discovery; -// filter is stateless -public interface DiscoveryFilter { +import org.springframework.core.Ordered; + +/** + * Server list filters for DiscoveryTree. + * + * Implementation Notice: DiscoveryFilter is initialized using bean and instance shared for all + * microservices. If implementation has states, can put the states to DiscoveryContext or parent DiscoveryTreeNode. + */ +public interface DiscoveryFilter extends Ordered { default boolean enabled() { return true; } - int getOrder(); - - // grouping filter, means grouping instances to some groups - // eg: operation/ZoneAware/transport + /** + * grouping filter, means grouping instances to some groups + * eg: operation/ZoneAware/transport + */ default boolean isGroupingFilter() { return false; } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTree.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTree.java index d4246266f67..c7b70f606b9 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTree.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTree.java @@ -17,24 +17,22 @@ package org.apache.servicecomb.registry.discovery; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Iterator; +import java.util.Collections; import java.util.List; +import java.util.Map; -import com.google.common.annotations.VisibleForTesting; import org.apache.servicecomb.foundation.common.cache.VersionedCache; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.registry.DiscoveryManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; /** - * help to understand DiscoveryTree *
  * DiscoveryTree is used to:
- * 1.get all instances by app/microserviceName/versionRule
+ * 1.get all instances by app/microserviceName
  * 2.filter all instances set, and output another set, the output set is instance or something else, this depend on filter set
  *
  * DiscoveryFilter have different types:
@@ -44,11 +42,9 @@
  * different types can composite in one filter
  *
  * features:
- * 1.every group combination(eg:1.0.0-2.0.0/1.0.0+/self/RESTful) relate to a loadBalancer instance
- * 2.if some filter output set is empty, DiscoveryTree can support try refilter logic
+ * 1.if some filter output set is empty, DiscoveryTree can support try refilter logic
  *   eg: if there is no available instances in self AZ, can refilter in other AZ
- *   red arrows in help to understand DiscoveryTree, show the refilter logic
- * 3.every filter must try to cache result, avoid calculate every time.
+ * 2.every filter must try to cache result, avoid calculate every time.
  *
  * usage:
  * 1.declare a field: DiscoveryTree discoveryTree = new DiscoveryTree();
@@ -77,90 +73,74 @@
 public class DiscoveryTree {
   private static final Logger LOGGER = LoggerFactory.getLogger(DiscoveryTree.class);
 
-  private volatile DiscoveryTreeNode root;
+  private final Map> root = new ConcurrentHashMapEx<>();
 
   private final Object lock = new Object();
 
-  private final List filters = new ArrayList<>();
+  private List filters = Collections.emptyList();
 
-  @VisibleForTesting
-  public void setRoot(DiscoveryTreeNode root) {
-    this.root = root;
-  }
-
-  @VisibleForTesting
-  public DiscoveryTreeNode getRoot() {
-    return root;
-  }
-
-  @VisibleForTesting
-  public List getFilters() {
-    return filters;
-  }
+  private final DiscoveryManager discoveryManager;
 
-  public void loadFromSPI(Class cls) {
-    filters.addAll(SPIServiceUtils.getSortedService(cls));
+  public DiscoveryTree(DiscoveryManager discoveryManager) {
+    this.discoveryManager = discoveryManager;
   }
 
-  public void addFilter(DiscoveryFilter filter) {
-    filters.add(filter);
+  @Autowired
+  public void setDiscoveryFilters(List filters) {
+    this.filters = filters;
+    log();
   }
 
-  // group name are qualifiedName
-  // all leaf group will create a loadbalancer instance, groupName is loadBalancer key
-  public void sort() {
-    filters.sort(Comparator.comparingInt(DiscoveryFilter::getOrder));
-
-    Iterator iterator = filters.iterator();
-    while (iterator.hasNext()) {
-      DiscoveryFilter filter = iterator.next();
-      if (!filter.enabled()) {
-        iterator.remove();
-      }
-      LOGGER.info("DiscoveryFilter {}, enabled {}.", filter.getClass().getName(), filter.enabled());
+  private void log() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("DiscoveryFilters(name, enabled, order, group):");
+    for (DiscoveryFilter filter : filters) {
+      sb.append("(").append(filter.getClass().getName()).append(",")
+          .append(filter.enabled()).append(",").append(filter.getOrder()).append(",")
+          .append(filter.isGroupingFilter()).append(")");
     }
+    LOGGER.info(sb.toString());
   }
 
-  protected boolean isMatch(VersionedCache existing, VersionedCache inputCache) {
+  boolean isMatch(VersionedCache existing, VersionedCache inputCache) {
     return existing != null && existing.isSameVersion(inputCache);
   }
 
-  protected boolean isExpired(VersionedCache existing, VersionedCache inputCache) {
+  boolean isExpired(VersionedCache existing, VersionedCache inputCache) {
     return existing == null || existing.isExpired(inputCache);
   }
 
-  public DiscoveryTreeNode discovery(DiscoveryContext context, String appId, String microserviceName,
-      String versionRule) {
-    VersionedCache instanceVersionedCache = DiscoveryManager.INSTANCE
-        .getInstanceCacheManager()
-        .getOrCreateVersionedCache(appId, microserviceName, versionRule);
+  public DiscoveryTreeNode discovery(DiscoveryContext context, String appId, String microserviceName) {
+    VersionedCache instanceVersionedCache = this.discoveryManager.getOrCreateVersionedCache(appId, microserviceName);
 
-    return discovery(context, instanceVersionedCache);
+    return discovery(appId, microserviceName, context, instanceVersionedCache);
   }
 
-  public DiscoveryTreeNode discovery(DiscoveryContext context, VersionedCache inputCache) {
-    DiscoveryTreeNode tmpRoot = getOrCreateRoot(inputCache);
+  DiscoveryTreeNode discovery(String appId, String microserviceName, DiscoveryContext context,
+      VersionedCache inputCache) {
+    DiscoveryTreeNode tmpRoot = getOrCreateRoot(appId, microserviceName, inputCache);
     DiscoveryTreeNode parent = tmpRoot.children()
         .computeIfAbsent(inputCache.name(), name -> new DiscoveryTreeNode().fromCache(inputCache));
     return doDiscovery(context, parent);
   }
 
-  protected DiscoveryTreeNode getOrCreateRoot(VersionedCache inputCache) {
-    DiscoveryTreeNode tmpRoot = root;
+  protected DiscoveryTreeNode getOrCreateRoot(String appId, String microserviceName, VersionedCache inputCache) {
+    DiscoveryTreeNode tmpRoot = root.computeIfAbsent(appId, k -> new ConcurrentHashMapEx<>()).get(microserviceName);
     if (isMatch(tmpRoot, inputCache)) {
       return tmpRoot;
     }
 
     synchronized (lock) {
-      if (isExpired(root, inputCache)) {
+      if (isExpired(tmpRoot, inputCache)) {
         // not initialized or inputCache newer than root, create new root
-        root = new DiscoveryTreeNode().cacheVersion(inputCache.cacheVersion());
-        return root;
+        tmpRoot = new DiscoveryTreeNode().cacheVersion(inputCache.cacheVersion());
+        root.get(appId).put(microserviceName, tmpRoot);
+        return tmpRoot;
       }
 
-      if (root.isSameVersion(inputCache)) {
+      if (tmpRoot.isSameVersion(inputCache)) {
         // reuse root directly
-        return root;
+        return tmpRoot;
       }
     }
 
@@ -170,12 +150,18 @@ protected DiscoveryTreeNode getOrCreateRoot(VersionedCache inputCache) {
     // 3) thread 1 go on, then root is newer than inputCache
     //    but if create old children in new version root, it's a wrong logic
     // so just create a temporary root for the inputCache, DO NOT assign to root
-    return new DiscoveryTreeNode().cacheVersion(inputCache.cacheVersion());
+    tmpRoot = new DiscoveryTreeNode().cacheVersion(inputCache.cacheVersion());
+    root.get(appId).put(microserviceName, tmpRoot);
+    return tmpRoot;
   }
 
   protected DiscoveryTreeNode doDiscovery(DiscoveryContext context, DiscoveryTreeNode parent) {
     for (int idx = 0; idx < filters.size(); ) {
       DiscoveryFilter filter = filters.get(idx);
+      if (!filter.enabled()) {
+        idx++;
+        continue;
+      }
       context.setCurrentNode(parent);
 
       DiscoveryTreeNode child = filter.discovery(context, parent);
@@ -199,7 +185,7 @@ protected DiscoveryTreeNode doDiscovery(DiscoveryContext context, DiscoveryTreeN
         }
 
         // no rerun support, go on even result is empty
-        // because maybe some filter use other mechanism to create a instance(eg:domain name)
+        // because maybe some filter use other mechanism to create an instance(eg:domain name)
       }
 
       parent = child;
diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTreeNode.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTreeNode.java
index 3dda15f6c4b..e0f7c828c10 100644
--- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTreeNode.java
+++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTreeNode.java
@@ -25,7 +25,7 @@
 public class DiscoveryTreeNode extends VersionedCache {
   private volatile boolean childrenInited;
 
-  private int level;
+  private volatile int level;
 
   protected Map attributes = new ConcurrentHashMapEx<>();
 
diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstancePing.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstancePing.java
new file mode 100644
index 00000000000..2aa45718194
--- /dev/null
+++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstancePing.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.servicecomb.registry.discovery;
+
+import org.springframework.core.Ordered;
+
+public interface InstancePing extends Ordered {
+  boolean ping(StatefulDiscoveryInstance instance);
+}
diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstanceStatusDiscoveryFilter.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstanceStatusDiscoveryFilter.java
index 5e2886f7ea4..e7a4029bdeb 100644
--- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstanceStatusDiscoveryFilter.java
+++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstanceStatusDiscoveryFilter.java
@@ -17,17 +17,22 @@
 
 package org.apache.servicecomb.registry.discovery;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
+import java.util.ArrayList;
+import java.util.List;
 
-import org.apache.servicecomb.registry.api.registry.MicroserviceInstance;
-import org.apache.servicecomb.registry.api.registry.MicroserviceInstanceStatus;
+import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus;
+import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.HistoryStatus;
+import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.IsolationStatus;
+import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.PingStatus;
 
-import com.netflix.config.DynamicPropertyFactory;
+public class InstanceStatusDiscoveryFilter extends AbstractGroupDiscoveryFilter {
+  public static final String PARAMETER = "status_level";
 
-public class InstanceStatusDiscoveryFilter extends AbstractDiscoveryFilter {
-  private static final String UP_INSTANCES = "upInstances";
+  public static final String GROUP_PREFIX = "status_group_";
+
+  public static final String GROUP_SIZE = "status_group_size";
+
+  public static final String SERVICECOMB_LOADBALANCE_FILTER_STATUS_ENABLED = "servicecomb.loadbalance.filter.status.enabled";
 
   @Override
   public int getOrder() {
@@ -36,35 +41,83 @@ public int getOrder() {
 
   @Override
   public boolean enabled() {
-    return DynamicPropertyFactory.getInstance()
-        .getBooleanProperty("servicecomb.loadbalance.filter.status.enabled", true).get();
+
+    if (enabled == null) {
+      enabled = dynamicProperties.getBooleanProperty(SERVICECOMB_LOADBALANCE_FILTER_STATUS_ENABLED,
+          value -> enabled = value,
+          true);
+    }
+    return enabled;
+  }
+
+  @Override
+  protected String groupsSizeParameter() {
+    return GROUP_SIZE;
   }
 
   @Override
-  public boolean isGroupingFilter() {
-    return true;
+  protected String contextParameter() {
+    return PARAMETER;
   }
 
   @Override
-  protected String findChildName(DiscoveryContext context, DiscoveryTreeNode parent) {
-    return UP_INSTANCES;
+  protected String groupPrefix() {
+    return GROUP_PREFIX;
   }
 
   @Override
   public void init(DiscoveryContext context, DiscoveryTreeNode parent) {
-    Map instances = parent.data();
-    Map filteredServers = new HashMap<>();
-    for (Entry instanceEntry : instances.entrySet()) {
-      MicroserviceInstance instance = instanceEntry.getValue();
-      if (MicroserviceInstanceStatus.UP == instance.getStatus()) {
-        filteredServers.put(instanceEntry.getKey(), instance);
+    List instances = parent.data();
+    List level0 = new ArrayList<>();
+    List level1 = new ArrayList<>();
+    List level2 = new ArrayList<>();
+    List level3 = new ArrayList<>();
+
+    int groups = 1;
+
+    for (StatefulDiscoveryInstance instance : instances) {
+      if (HistoryStatus.CURRENT == instance.getHistoryStatus() &&
+          MicroserviceInstanceStatus.UP == instance.getStatus() &&
+          PingStatus.OK == instance.getPingStatus() &&
+          IsolationStatus.NORMAL == instance.getIsolationStatus()) {
+        level0.add(instance);
+        continue;
+      }
+      if (HistoryStatus.CURRENT == instance.getHistoryStatus() &&
+          MicroserviceInstanceStatus.UP == instance.getStatus() &&
+          PingStatus.UNKNOWN == instance.getPingStatus() &&
+          IsolationStatus.NORMAL == instance.getIsolationStatus()) {
+        level1.add(instance);
+        continue;
+      }
+      if (HistoryStatus.HISTORY == instance.getHistoryStatus() &&
+          MicroserviceInstanceStatus.UP == instance.getStatus() &&
+          PingStatus.OK == instance.getPingStatus() &&
+          IsolationStatus.NORMAL == instance.getIsolationStatus()) {
+        level2.add(instance);
+        continue;
       }
+      level3.add(instance);
     }
 
-    if (filteredServers.isEmpty()) {
-      return;
+    if (!level0.isEmpty()) {
+      parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode()
+          .subName(parent, GROUP_PREFIX + groups).data(level0));
+      groups++;
     }
-    DiscoveryTreeNode child = new DiscoveryTreeNode().subName(parent, UP_INSTANCES).data(filteredServers);
-    parent.child(UP_INSTANCES, child);
+    if (!level1.isEmpty()) {
+      parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode()
+          .subName(parent, GROUP_PREFIX + groups).data(level1));
+      groups++;
+    }
+    if (!level2.isEmpty()) {
+      parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode()
+          .subName(parent, GROUP_PREFIX + groups).data(level2));
+      groups++;
+    }
+    parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode()
+        .subName(parent, GROUP_PREFIX + groups).data(level3));
+
+    parent.attribute(GROUP_SIZE, groups);
   }
 }
diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/MicroserviceInstanceCache.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/MicroserviceInstanceCache.java
new file mode 100644
index 00000000000..4990b3d8f90
--- /dev/null
+++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/MicroserviceInstanceCache.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.registry.discovery;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.servicecomb.foundation.common.cache.VersionedCache;
+import org.apache.servicecomb.registry.DiscoveryManager;
+import org.apache.servicecomb.registry.api.DiscoveryInstance;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.CollectionUtils;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+
+/**
+ * 在公钥认证场景,需要查询对端的实例信息。
+ *
+ * 微服务实例缓存 key 为:serviceId@instanceId 缓存limit:1000 缓存老化策略:30分钟没有访问就过期。
+ *
+ */
+public class MicroserviceInstanceCache {
+
+  private static final Logger logger = LoggerFactory.getLogger(MicroserviceInstanceCache.class);
+
+  private static final Cache instances = CacheBuilder.newBuilder()
+      .maximumSize(1000)
+      .expireAfterAccess(30, TimeUnit.MINUTES)
+      .build();
+
+  private final DiscoveryManager discoveryManager;
+
+  public MicroserviceInstanceCache(DiscoveryManager discoveryManager) {
+    this.discoveryManager = discoveryManager;
+  }
+
+  public DiscoveryInstance getOrCreate(String application, String serviceName) {
+    try {
+      String key = String.format("%s@%s", application, serviceName);
+      return instances.get(key, () -> {
+        VersionedCache instances = discoveryManager.getOrCreateVersionedCache(application, serviceName);
+        List statefulDiscoveryInstances = instances.data();
+        if (CollectionUtils.isEmpty(statefulDiscoveryInstances)) {
+          throw new IllegalArgumentException("instance id not exists.");
+        }
+        return statefulDiscoveryInstances.get(0);
+      });
+    } catch (ExecutionException | UncheckedExecutionException e) {
+      logger.error("get microservice instance from cache failed, {}, {}",
+          String.format("%s@%s", application, serviceName),
+          e.getMessage());
+      return null;
+    }
+  }
+}
diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/StatefulDiscoveryInstance.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/StatefulDiscoveryInstance.java
new file mode 100644
index 00000000000..394b2b03ccc
--- /dev/null
+++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/StatefulDiscoveryInstance.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.servicecomb.registry.discovery;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicecomb.registry.api.AbstractDiscoveryInstance;
+import org.apache.servicecomb.registry.api.DataCenterInfo;
+import org.apache.servicecomb.registry.api.DiscoveryInstance;
+import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus;
+
+/**
+ * Wrapper class for DiscoveryInstance with states like: isolation, health, metrics and so on.
+ */
+public class StatefulDiscoveryInstance extends AbstractDiscoveryInstance {
+  public enum IsolationStatus {
+    NORMAL,
+    ISOLATED
+  }
+
+  public enum PingStatus {
+    UNKNOWN,
+    OK,
+    FAIL
+  }
+
+  public enum HistoryStatus {
+    CURRENT,
+    HISTORY
+  }
+
+  private final DiscoveryInstance discoveryInstance;
+
+  private IsolationStatus isolationStatus = IsolationStatus.NORMAL;
+
+  private long isolatedTime;
+
+  private long isolateDuration;
+
+  private PingStatus pingStatus = PingStatus.UNKNOWN;
+
+  private long pingTime = System.currentTimeMillis();
+
+  private HistoryStatus historyStatus = HistoryStatus.CURRENT;
+
+  public StatefulDiscoveryInstance(DiscoveryInstance discoveryInstance) {
+    this.discoveryInstance = discoveryInstance;
+  }
+
+  public IsolationStatus getIsolationStatus() {
+    return isolationStatus;
+  }
+
+  public void setIsolationStatus(
+      IsolationStatus isolationStatus) {
+    this.isolationStatus = isolationStatus;
+  }
+
+  public PingStatus getPingStatus() {
+    return pingStatus;
+  }
+
+  public void setPingStatus(PingStatus pingStatus) {
+    this.pingStatus = pingStatus;
+  }
+
+  public HistoryStatus getHistoryStatus() {
+    return historyStatus;
+  }
+
+  public void setHistoryStatus(HistoryStatus historyStatus) {
+    this.historyStatus = historyStatus;
+  }
+
+  public long getIsolatedTime() {
+    return isolatedTime;
+  }
+
+  public void setIsolatedTime(long isolatedTime) {
+    this.isolatedTime = isolatedTime;
+  }
+
+  public long getIsolateDuration() {
+    return isolateDuration;
+  }
+
+  public void setIsolateDuration(long isolateDuration) {
+    this.isolateDuration = isolateDuration;
+  }
+
+  public long getPingTime() {
+    return pingTime;
+  }
+
+  public void setPingTime(long pingTime) {
+    this.pingTime = pingTime;
+  }
+
+  @Override
+  public MicroserviceInstanceStatus getStatus() {
+    return this.discoveryInstance.getStatus();
+  }
+
+  @Override
+  public String getRegistryName() {
+    return this.discoveryInstance.getRegistryName();
+  }
+
+  @Override
+  public String getEnvironment() {
+    return this.discoveryInstance.getEnvironment();
+  }
+
+  @Override
+  public String getApplication() {
+    return this.discoveryInstance.getApplication();
+  }
+
+  @Override
+  public String getServiceName() {
+    return this.discoveryInstance.getServiceName();
+  }
+
+  @Override
+  public String getAlias() {
+    return this.discoveryInstance.getAlias();
+  }
+
+  @Override
+  public String getVersion() {
+    return this.discoveryInstance.getVersion();
+  }
+
+  @Override
+  public DataCenterInfo getDataCenterInfo() {
+    return this.discoveryInstance.getDataCenterInfo();
+  }
+
+  @Override
+  public String getDescription() {
+    return this.discoveryInstance.getDescription();
+  }
+
+  @Override
+  public Map getProperties() {
+    return this.discoveryInstance.getProperties();
+  }
+
+  @Override
+  public Map getSchemas() {
+    return this.discoveryInstance.getSchemas();
+  }
+
+  @Override
+  public List getEndpoints() {
+    return this.discoveryInstance.getEndpoints();
+  }
+
+  @Override
+  public String getInstanceId() {
+    return this.discoveryInstance.getInstanceId();
+  }
+
+  @Override
+  public String getServiceId() {
+    return this.discoveryInstance.getServiceId();
+  }
+}
diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/TelnetInstancePing.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/TelnetInstancePing.java
new file mode 100644
index 00000000000..07967579a3b
--- /dev/null
+++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/TelnetInstancePing.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.servicecomb.registry.discovery;
+
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import org.apache.servicecomb.foundation.common.net.IpPort;
+import org.apache.servicecomb.foundation.common.net.NetUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.CollectionUtils;
+
+public class TelnetInstancePing implements InstancePing {
+  private static final Logger LOGGER = LoggerFactory.getLogger(TelnetInstancePing.class);
+
+  @Override
+  public boolean ping(StatefulDiscoveryInstance instance) {
+    if (CollectionUtils.isEmpty(instance.getEndpoints())) {
+      return false;
+    }
+
+    for (String endpoint : instance.getEndpoints()) {
+      IpPort ipPort = NetUtils.parseIpPortFromURI(endpoint);
+      try (Socket s = new Socket()) {
+        s.connect(new InetSocketAddress(ipPort.getHostOrIp(), ipPort.getPort()), 3000);
+        return true;
+      } catch (Exception e) {
+        LOGGER.warn("ping instance {}/{}/{}/{} endpoint {} failed",
+            instance.getApplication(),
+            instance.getServiceName(),
+            instance.getRegistryName(),
+            instance.getInstanceId(), endpoint);
+      }
+    }
+
+    return false;
+  }
+
+  @Override
+  public int getOrder() {
+    return 10000;
+  }
+}
diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/swagger/SwaggerLoader.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/swagger/SwaggerLoader.java
deleted file mode 100644
index 8d08d24e286..00000000000
--- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/swagger/SwaggerLoader.java
+++ /dev/null
@@ -1,204 +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.servicecomb.registry.swagger;
-
-import java.net.URI;
-import java.net.URL;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-import org.apache.commons.io.FilenameUtils;
-import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
-import org.apache.servicecomb.foundation.common.utils.JvmUtils;
-import org.apache.servicecomb.foundation.common.utils.ResourceUtil;
-import org.apache.servicecomb.registry.DiscoveryManager;
-import org.apache.servicecomb.registry.RegistrationManager;
-import org.apache.servicecomb.registry.api.registry.Microservice;
-import org.apache.servicecomb.registry.api.registry.MicroserviceInstance;
-import org.apache.servicecomb.registry.definition.MicroserviceNameParser;
-import org.apache.servicecomb.swagger.SwaggerUtils;
-import org.apache.servicecomb.swagger.generator.SwaggerGenerator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Charsets;
-import com.google.common.hash.Hashing;
-
-import io.swagger.models.Swagger;
-
-public class SwaggerLoader {
-  private static final Logger LOGGER = LoggerFactory.getLogger(SwaggerLoader.class);
-
-  // first key : appId
-  // second key: microservice short name
-  // third key : schemaId
-  private final Map>> apps = new ConcurrentHashMapEx<>();
-
-  public SwaggerLoader() {
-  }
-
-  // result length is 64
-  public static String calcSchemaSummary(String schemaContent) {
-    return Hashing.sha256().newHasher().putString(schemaContent, Charsets.UTF_8).hash().toString();
-  }
-
-  /**
-   * 
-   * register swaggers in the location to current microservice
-   * Scenes for contract first mode:
-   *  1.consumer
-   *    manager manage some product, can only know product microservice names after deploy
-   *    and can only register swagger after product registered
-   *    in fact, consumers can load swagger from ServiceCenter
-   *    so for consumer, this logic is not necessary, just keep it for compatible
-   *  2.producer
-   *    deploy to different microservice name in different product
-   *    can register swaggers in BootListener.onBeforeProducerProvider
-   * 
- * @param swaggersLocation eg. "test/schemas", will load all test/schemas/*.yaml - */ - public void registerSwaggersInLocation(String swaggersLocation) { - String microserviceName = RegistrationManager.INSTANCE.getMicroservice().getServiceName(); - registerSwaggersInLocation(microserviceName, swaggersLocation); - } - - public void registerSwaggersInLocation(String microserviceName, String swaggersLocation) { - LOGGER.info("register schemas in location [{}], microserviceName=[{}]", swaggersLocation, microserviceName); - try { - List resourceUris = ResourceUtil.findResourcesBySuffix(swaggersLocation, ".yaml"); - if (resourceUris.isEmpty()) { - LOGGER.error("register swagger in not exist location: \"{}\".", swaggersLocation); - return; - } - for (URI uri : resourceUris) { - URL url = uri.toURL(); - Swagger swagger = SwaggerUtils.parseAndValidateSwagger(url); - String schemaId = FilenameUtils.getBaseName(url.getPath()); - registerSwagger(microserviceName, schemaId, swagger); - } - } catch (Throwable e) { - throw new IllegalStateException(String.format( - "failed to register swaggers, microserviceName=%s, location=%s.", microserviceName, swaggersLocation), - e); - } - } - - public void registerSwagger(String microserviceName, String schemaId, Swagger swagger) { - MicroserviceNameParser parser = new MicroserviceNameParser( - RegistrationManager.INSTANCE.getMicroservice().getAppId(), microserviceName); - registerSwagger(parser.getAppId(), parser.getShortName(), schemaId, swagger); - } - - public Swagger registerSwagger(String appId, String shortName, String schemaId, Class cls) { - Swagger swagger = SwaggerGenerator.generate(cls); - registerSwagger(appId, shortName, schemaId, swagger); - return swagger; - } - - public void registerSwagger(String appId, String shortName, String schemaId, Swagger swagger) { - apps.computeIfAbsent(appId, k -> new ConcurrentHashMapEx<>()) - .computeIfAbsent(shortName, k -> new ConcurrentHashMapEx<>()) - .put(schemaId, swagger); - LOGGER.info("register swagger appId={}, name={}, schemaId={}.", appId, shortName, schemaId); - } - - public void unregisterSwagger(String appId, String shortName, String schemaId) { - apps.getOrDefault(appId, Collections.emptyMap()) - .getOrDefault(shortName, Collections.emptyMap()) - .remove(schemaId); - } - - public Swagger loadSwagger(Microservice microservice, Collection instances, String schemaId) { - Swagger swagger = loadLocalSwagger(microservice.getAppId(), microservice.getServiceName(), schemaId); - if (swagger != null) { - return swagger; - } - - return loadFromRemote(microservice, instances, schemaId); - } - - public Swagger loadLocalSwagger(String appId, String shortName, String schemaId) { - LOGGER.info("try to load schema locally, appId=[{}], serviceName=[{}], schemaId=[{}]", - appId, shortName, schemaId); - Swagger swagger = loadFromMemory(appId, shortName, schemaId); - if (swagger != null) { - LOGGER.info("load schema from memory"); - return swagger; - } - - return loadFromResource(appId, shortName, schemaId); - } - - @VisibleForTesting - public Swagger loadFromMemory(String appId, String shortName, String schemaId) { - return Optional.ofNullable(apps.get(appId)) - .map(microservices -> microservices.get(shortName)) - .map(schemas -> schemas.get(schemaId)) - .orElse(null); - } - - private Swagger loadFromResource(String appId, String shortName, String schemaId) { - if (appId.equals(RegistrationManager.INSTANCE.getMicroservice().getAppId())) { - Swagger swagger = loadFromResource(String.format("microservices/%s/%s.yaml", shortName, schemaId)); - if (swagger != null) { - return swagger; - } - } - - return loadFromResource(String.format("applications/%s/%s/%s.yaml", appId, shortName, schemaId)); - } - - private Swagger loadFromResource(String path) { - URL url = JvmUtils.findClassLoader().getResource(path); - if (url == null) { - return null; - } - - LOGGER.info("load schema from path [{}]", path); - return SwaggerUtils.parseAndValidateSwagger(url); - } - - private Swagger loadFromRemote(Microservice microservice, Collection instances, - String schemaId) { - String schemaContent = DiscoveryManager.INSTANCE.getSchema(microservice.getServiceId(), instances, schemaId); - if (schemaContent != null) { - LOGGER.info( - "load schema from service center, appId={}, microserviceName={}, version={}, serviceId={}, schemaId={}.", - microservice.getAppId(), - microservice.getServiceName(), - microservice.getVersion(), - microservice.getServiceId(), - schemaId); - LOGGER.debug(schemaContent); - return SwaggerUtils.parseAndValidateSwagger(schemaContent); - } - - LOGGER.warn("no schema in local, and can not get schema from service center, " - + "appId={}, microserviceName={}, version={}, serviceId={}, schemaId={}.", - microservice.getAppId(), - microservice.getServiceName(), - microservice.getVersion(), - microservice.getServiceId(), - schemaId); - - return null; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRule.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRule.java deleted file mode 100644 index b5a3458ab40..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRule.java +++ /dev/null @@ -1,46 +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.servicecomb.registry.version; - -import org.apache.servicecomb.foundation.common.Version; - -public abstract class VersionRule { - private final String versionRule; - - public VersionRule(String versionRule) { - this.versionRule = versionRule; - } - - public String getVersionRule() { - return versionRule; - } - - // equals isMatch except "latest" rule - public boolean isAccept(Version version) { - return isMatch(version, null); - } - - // latestVersion is only required for "latest" rule - // it's ugly, but simpler - public abstract boolean isMatch(Version version, Version latestVersion); - - @Override - public String toString() { - return versionRule; - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleFixedParser.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleFixedParser.java deleted file mode 100644 index 38fa3809bcc..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleFixedParser.java +++ /dev/null @@ -1,44 +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.servicecomb.registry.version; - -import org.apache.servicecomb.foundation.common.Version; - -// fixed, this should be the last parser -// will not check version format -public class VersionRuleFixedParser implements VersionRuleParser { - static class FixedVersionRule extends VersionRule { - private final Version version; - - public FixedVersionRule(String versionRule, Version version) { - super(versionRule); - - this.version = version; - } - - public boolean isMatch(Version version, Version latestVersion) { - return this.version.equals(version); - } - } - - @Override - public VersionRule parse(String strVersionRule) { - Version version = new Version(strVersionRule); - return new FixedVersionRule(version.getVersion(), version); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleLatestParser.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleLatestParser.java deleted file mode 100644 index 05331845322..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleLatestParser.java +++ /dev/null @@ -1,47 +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.servicecomb.registry.version; - -import org.apache.servicecomb.foundation.common.Version; -import org.apache.servicecomb.registry.definition.DefinitionConst; - -public class VersionRuleLatestParser implements VersionRuleParser { - static class LatestVersionRule extends VersionRule { - public LatestVersionRule(String versionRule) { - super(versionRule); - } - - @Override - public boolean isAccept(Version version) { - return true; - } - - public boolean isMatch(Version version, Version latestVersion) { - return version.equals(latestVersion); - } - } - - @Override - public VersionRule parse(String strVersionRule) { - if (!DefinitionConst.VERSION_RULE_LATEST.equals(strVersionRule)) { - return null; - } - - return new LatestVersionRule(strVersionRule); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleParser.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleParser.java deleted file mode 100644 index 29bdd1c10fc..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleParser.java +++ /dev/null @@ -1,23 +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.servicecomb.registry.version; - -public interface VersionRuleParser { - // if can not parse, then return null - VersionRule parse(String strVersionRule); -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleRangeParser.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleRangeParser.java deleted file mode 100644 index 182cb4c36f4..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleRangeParser.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.servicecomb.registry.version; - -import org.apache.servicecomb.foundation.common.Version; - -// 1.0.0-2.0.0 -public class VersionRuleRangeParser implements VersionRuleParser { - static class RangeVersionRule extends VersionRule { - private final Version from; - - private final Version to; - - public RangeVersionRule(String versionRule, Version from, Version to) { - super(versionRule); - - this.from = from; - this.to = to; - } - - public boolean isMatch(Version version, Version latestVersion) { - return version.compareTo(from) >= 0 && version.compareTo(to) < 0; - } - } - - @Override - public VersionRule parse(String strVersionRule) { - int pos = strVersionRule.indexOf('-'); - if (pos <= 0 || pos == strVersionRule.length() - 1) { - return null; - } - - Version from = new Version(strVersionRule.substring(0, pos)); - Version to = new Version(strVersionRule.substring(pos + 1)); - return new RangeVersionRule(from.getVersion() + "-" + to.getVersion(), from, to); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleStartFromParser.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleStartFromParser.java deleted file mode 100644 index d657e701e67..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleStartFromParser.java +++ /dev/null @@ -1,48 +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.servicecomb.registry.version; - -import org.apache.servicecomb.foundation.common.Version; - -// 1.0.0+ -public class VersionRuleStartFromParser implements VersionRuleParser { - static class StartFromVersionRule extends VersionRule { - private final Version from; - - public StartFromVersionRule(String versionRule, Version from) { - super(versionRule); - - this.from = from; - } - - public boolean isMatch(Version version, Version latestVersion) { - return version.compareTo(from) >= 0; - } - } - - @Override - public VersionRule parse(String strVersionRule) { - int pos = strVersionRule.indexOf('+'); - if (pos <= 0 || pos != strVersionRule.length() - 1) { - return null; - } - - Version from = new Version(strVersionRule.substring(0, pos)); - return new StartFromVersionRule(from.getVersion() + "+", from); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleUtils.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleUtils.java deleted file mode 100644 index fe72454bfb4..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionRuleUtils.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.servicecomb.registry.version; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; - -public final class VersionRuleUtils { - private static final List parsers = new ArrayList<>(); - - private static final Map versionRuleCache = new ConcurrentHashMapEx<>(); - - static { - parsers.add(new VersionRuleLatestParser()); - parsers.add(new VersionRuleStartFromParser()); - parsers.add(new VersionRuleRangeParser()); - parsers.add(new VersionRuleFixedParser()); - } - - //1.0 - //1.0.0+ - //1.0.0-2.0.0 - //latest - public static VersionRule getOrCreate(String strVersionRule) { - Objects.requireNonNull(strVersionRule); - - return versionRuleCache.computeIfAbsent(strVersionRule, VersionRuleUtils::create); - } - - public static VersionRule create(String strVersionRule) { - strVersionRule = strVersionRule.trim(); - for (VersionRuleParser parser : parsers) { - VersionRule versionRule = parser.parse(strVersionRule); - if (versionRule != null) { - return versionRule; - } - } - - throw new IllegalStateException("never run to here"); - } -} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionUtils.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionUtils.java deleted file mode 100644 index a85cf03b48e..00000000000 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/version/VersionUtils.java +++ /dev/null @@ -1,38 +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.servicecomb.registry.version; - -import java.util.Map; -import java.util.Objects; - -import org.apache.servicecomb.foundation.common.Version; -import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; - -public final class VersionUtils { - private static final Map versionCache = new ConcurrentHashMapEx<>(); - - public static Version getOrCreate(String strVersion) { - Objects.requireNonNull(strVersion); - - return versionCache.computeIfAbsent(strVersion, Version::new); - } - - public static void clear() { - versionCache.clear(); - } -} diff --git a/foundations/foundation-registry/src/main/resources/META-INF/services/org.apache.servicecomb.registry.api.Versions b/foundations/foundation-registry/src/main/resources/META-INF/services/org.apache.servicecomb.registry.api.Versions deleted file mode 100644 index faf43b019b7..00000000000 --- a/foundations/foundation-registry/src/main/resources/META-INF/services/org.apache.servicecomb.registry.api.Versions +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.registry.api.registry.ServiceCombVersion diff --git a/foundations/foundation-registry/src/main/resources/META-INF/services/org.apache.servicecomb.registry.consumer.MicroserviceInstancePing b/foundations/foundation-registry/src/main/resources/META-INF/services/org.apache.servicecomb.registry.consumer.MicroserviceInstancePing index 8493c8a6e17..56169c4507c 100644 --- a/foundations/foundation-registry/src/main/resources/META-INF/services/org.apache.servicecomb.registry.consumer.MicroserviceInstancePing +++ b/foundations/foundation-registry/src/main/resources/META-INF/services/org.apache.servicecomb.registry.consumer.MicroserviceInstancePing @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.registry.consumer.SimpleMicroserviceInstancePing \ No newline at end of file +org.apache.servicecomb.registry.consumer.SimpleMicroserviceInstancePing diff --git a/foundations/foundation-registry/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/foundations/foundation-registry/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..8296569485a --- /dev/null +++ b/foundations/foundation-registry/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.registry.RegistryConfiguration diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/TestDiscoveryManager.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/TestDiscoveryManager.java new file mode 100644 index 00000000000..bd50cc85748 --- /dev/null +++ b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/TestDiscoveryManager.java @@ -0,0 +1,212 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.cache.VersionedCache; +import org.apache.servicecomb.registry.api.AbstractDiscoveryInstance; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.Discovery; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.HistoryStatus; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.IsolationStatus; +import org.apache.servicecomb.registry.discovery.TelnetInstancePing; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestDiscoveryManager { + + @Test + public void test_initial_service_list_correct() { + MyDiscovery discovery1 = Mockito.spy(new MyDiscovery()); + MyDiscoveryInstance instance1 = Mockito.mock(MyDiscoveryInstance.class); + DiscoveryManager discoveryManager = new DiscoveryManager(List.of(discovery1), List.of(new TelnetInstancePing())); + Mockito.when(discovery1.findServiceInstances("app", "service")) + .thenReturn(List.of(instance1)); + Mockito.when(instance1.getInstanceId()).thenReturn("instance1"); + //first read + VersionedCache versionedCache = discoveryManager.getOrCreateVersionedCache("app", "service"); + List result = versionedCache.data(); + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals("instance1", result.get(0).getInstanceId()); + Assertions.assertEquals(HistoryStatus.CURRENT, result.get(0).getHistoryStatus()); + // second read + versionedCache = discoveryManager.getOrCreateVersionedCache("app", "service"); + result = versionedCache.data(); + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals("instance1", result.get(0).getInstanceId()); + Assertions.assertEquals(HistoryStatus.CURRENT, result.get(0).getHistoryStatus()); + } + + @Test + public void test_initial_empty_service_list_correct() { + MyDiscovery discovery1 = Mockito.spy(new MyDiscovery()); + DiscoveryManager discoveryManager = new DiscoveryManager(List.of(discovery1), List.of(new TelnetInstancePing())); + Mockito.when(discovery1.findServiceInstances("app", "service")) + .thenReturn(Collections.emptyList()); + //first read + VersionedCache versionedCache = discoveryManager.getOrCreateVersionedCache("app", "service"); + List result = versionedCache.data(); + Assertions.assertEquals(0, result.size()); + // second read + discoveryManager.getOrCreateVersionedCache("app", "service"); + result = versionedCache.data(); + Assertions.assertEquals(0, result.size()); + } + + @Test + public void test_isolate_service_instance_correct() { + MyDiscovery discovery1 = Mockito.spy(new MyDiscovery()); + MyDiscoveryInstance instance1 = Mockito.mock(MyDiscoveryInstance.class); + DiscoveryManager discoveryManager = new DiscoveryManager(List.of(discovery1), List.of(new TelnetInstancePing())); + Mockito.when(discovery1.findServiceInstances("app", "service")) + .thenReturn(List.of(instance1)); + Mockito.when(instance1.getInstanceId()).thenReturn("instance1"); + Mockito.when(instance1.getApplication()).thenReturn("app"); + Mockito.when(instance1.getServiceName()).thenReturn("service"); + + VersionedCache versionedCache = discoveryManager.getOrCreateVersionedCache("app", "service"); + List result = versionedCache.data(); + discoveryManager.onInstanceIsolated(result.get(0), 10000L); + versionedCache = discoveryManager.getOrCreateVersionedCache("app", "service"); + result = versionedCache.data(); + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals("instance1", result.get(0).getInstanceId()); + Assertions.assertEquals(HistoryStatus.CURRENT, result.get(0).getHistoryStatus()); + Assertions.assertEquals(IsolationStatus.ISOLATED, result.get(0).getIsolationStatus()); + } + + static class MyDiscovery implements Discovery { + + @Override + public String name() { + return "my"; + } + + @Override + public boolean enabled(String application, String serviceName) { + return true; + } + + @Override + public List findServiceInstances(String application, String serviceName) { + return null; + } + + @Override + public List findServices(String application) { + return null; + } + + @Override + public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { + + } + + @Override + public void init() { + + } + + @Override + public void run() { + + } + + @Override + public void destroy() { + + } + + @Override + public boolean enabled() { + return true; + } + } + + static class MyDiscoveryInstance extends AbstractDiscoveryInstance { + @Override + public MicroserviceInstanceStatus getStatus() { + return null; + } + + @Override + public String getRegistryName() { + return null; + } + + @Override + public String getEnvironment() { + return null; + } + + @Override + public String getApplication() { + return null; + } + + @Override + public String getServiceName() { + return null; + } + + @Override + public String getAlias() { + return null; + } + + @Override + public String getVersion() { + return null; + } + + @Override + public DataCenterInfo getDataCenterInfo() { + return null; + } + + @Override + public String getDescription() { + return null; + } + + @Override + public Map getProperties() { + return null; + } + + @Override + public Map getSchemas() { + return null; + } + + @Override + public List getEndpoints() { + return null; + } + + @Override + public String getInstanceId() { + return null; + } + } +} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/api/registry/TestServiceCombVersion.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/api/registry/TestServiceCombVersion.java deleted file mode 100644 index 3bbfbf6cdb1..00000000000 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/api/registry/TestServiceCombVersion.java +++ /dev/null @@ -1,31 +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.servicecomb.registry.api.registry; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestServiceCombVersion { - @Test - public void testServiceCombVersion() { - ServiceCombVersion version = new ServiceCombVersion(); - // this.getClass().getPackage().getImplementationVersion() - // is not stable, may get null or 2.0.1-SNAPSHOT - Assertions.assertTrue(version.loadVersion().toString().contains("ServiceComb")); - } -} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/config/TestAbstractPropertiesLoader.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/config/TestAbstractPropertiesLoader.java deleted file mode 100644 index b5b28f08230..00000000000 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/config/TestAbstractPropertiesLoader.java +++ /dev/null @@ -1,42 +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.servicecomb.registry.config; - -import org.apache.commons.configuration.Configuration; -import org.apache.servicecomb.config.BootStrapProperties; - -import com.netflix.config.DynamicConfiguration; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestAbstractPropertiesLoader { - @Test - public void testExtendedClassCompatible() { - Configuration configuration = new DynamicConfiguration(); - configuration.setProperty(BootStrapProperties.CONFIG_SERVICE_EXTENDED_CLASS, "invalidClass"); - - AbstractPropertiesLoader loader = MicroservicePropertiesLoader.INSTANCE; - try { - loader.loadProperties(configuration); - Assertions.fail("Must throw exception"); - } catch (Error e) { - Assertions.assertEquals(ClassNotFoundException.class, e.getCause().getClass()); - Assertions.assertEquals("invalidClass", e.getCause().getMessage()); - } - } -} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestAbstractTransportDiscoveryFilter.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestAbstractTransportDiscoveryFilter.java index c29e7d639f0..4a0c04140e4 100644 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestAbstractTransportDiscoveryFilter.java +++ b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestAbstractTransportDiscoveryFilter.java @@ -19,16 +19,15 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.UUID; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; +import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; public class TestAbstractTransportDiscoveryFilter { class AbstractEndpointDiscoveryFilterForTest extends AbstractEndpointDiscoveryFilter { @@ -44,7 +43,7 @@ protected String findTransportName(DiscoveryContext context, DiscoveryTreeNode p @Override protected Object createEndpoint(DiscoveryContext context, String transportName, String endpoint, - MicroserviceInstance instance) { + StatefulDiscoveryInstance instance) { if (disableCreate) { return null; } @@ -88,7 +87,7 @@ public void isTransportNameMatch_notEquals() { @Test public void discoveryNotExist() { transportName = "notExist"; - parent.data(Collections.emptyMap()); + parent.data(Collections.emptyList()); result = filter.discovery(context, parent); Assertions.assertTrue(result.isEmpty()); @@ -104,43 +103,47 @@ public void discoveryExist() { Assertions.assertSame(child, result); } - private MicroserviceInstance createInstance(String... schemas) { + private StatefulDiscoveryInstance createInstance(String... schemas) { String id = UUID.randomUUID().toString(); - MicroserviceInstance instance = new MicroserviceInstance(); - instance.setInstanceId(id); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance = new StatefulDiscoveryInstance(discoveryInstance); + List endpoints = new ArrayList<>(); for (int idx = 0; idx < schemas.length; idx++) { String schema = schemas[idx]; - instance.getEndpoints().add(String.format("%s://%s:%d", schema, id, 8080 + idx)); + endpoints.add(String.format("%s://%s:%d", schema, id, 8080 + idx)); } + Mockito.when(discoveryInstance.getInstanceId()).thenReturn(id); + Mockito.when(discoveryInstance.getEndpoints()).thenReturn(endpoints); return instance; } - private Map createMicroserviceInstances(MicroserviceInstance... instances) { - Map map = new LinkedHashMap<>(); - for (MicroserviceInstance instance : instances) { - map.put(instance.getInstanceId(), instance); + private List createMicroserviceInstances(StatefulDiscoveryInstance... instances) { + List result = new ArrayList<>(); + for (StatefulDiscoveryInstance instance : instances) { + result.add(instance); } - return map; + return result; } @Test public void createDiscoveryTree_oneTransport() { - MicroserviceInstance instance1 = createInstance("a", "b"); - MicroserviceInstance instance2 = createInstance("b"); - Map instances = createMicroserviceInstances(instance1, instance2); + StatefulDiscoveryInstance instance1 = createInstance("a", "b"); + StatefulDiscoveryInstance instance2 = createInstance("b"); + List instances = createMicroserviceInstances(instance1, instance2); parent.data(instances); result = filter.createDiscoveryTreeNode("a", context, parent); Assertions.assertEquals("parent/a", result.name()); - MatcherAssert.assertThat(result.collectionData(), Matchers.contains(instance1.getEndpoints().get(0))); + MatcherAssert.assertThat(result.collectionData(), + Matchers.contains(instance1.getEndpoints().get(0))); } @Test public void createDiscoveryTree_allTransport() { - MicroserviceInstance instance1 = createInstance("a", "b"); - MicroserviceInstance instance2 = createInstance("b"); - Map instances = createMicroserviceInstances(instance1, instance2); + StatefulDiscoveryInstance instance1 = createInstance("a", "b"); + StatefulDiscoveryInstance instance2 = createInstance("b"); + List instances = createMicroserviceInstances(instance1, instance2); parent.data(instances); result = filter.createDiscoveryTreeNode("", context, parent); @@ -155,22 +158,23 @@ public void createDiscoveryTree_allTransport() { @Test public void createDiscoveryTree_ignoreInvalid() { - MicroserviceInstance instance1 = createInstance("a", "b"); - MicroserviceInstance instance2 = createInstance(""); - Map instances = createMicroserviceInstances(instance1, instance2); + StatefulDiscoveryInstance instance1 = createInstance("a", "b"); + StatefulDiscoveryInstance instance2 = createInstance(""); + List instances = createMicroserviceInstances(instance1, instance2); parent.data(instances); result = filter.createDiscoveryTreeNode("", context, parent); Assertions.assertEquals("parent/", result.name()); - MatcherAssert.assertThat(result.collectionData(), Matchers.contains(instance1.getEndpoints().toArray())); + MatcherAssert.assertThat(result.collectionData(), + Matchers.contains(instance1.getEndpoints().toArray())); } @Test public void createEndpointNull() { disableCreate = true; - MicroserviceInstance instance1 = createInstance("a", "b"); - Map instances = createMicroserviceInstances(instance1); + StatefulDiscoveryInstance instance1 = createInstance("a", "b"); + List instances = createMicroserviceInstances(instance1); parent.data(instances); result = filter.createDiscoveryTreeNode("", context, parent); diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTree.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTree.java index 4aed1423832..d726d1b59c0 100644 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTree.java +++ b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTree.java @@ -18,112 +18,80 @@ package org.apache.servicecomb.registry.discovery; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; -import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.config.DynamicProperties; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.cache.InstanceCacheManager; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; 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 org.mockito.MockedStatic; import org.mockito.Mockito; - public class TestDiscoveryTree { - @BeforeEach - public void before() { - ConfigUtil.installDynamicConfig(); - } - @AfterEach - public void tearDown() { - ArchaiusUtils.resetConfig(); - } - - DiscoveryTree discoveryTree = new DiscoveryTree(); - - List filters = discoveryTree.getFilters(); - DiscoveryContext context = new DiscoveryContext(); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent"); DiscoveryTreeNode result; - @Test - public void loadFromSPI() { - DiscoveryFilter f1 = Mockito.mock(DiscoveryFilter.class); - DiscoveryFilter f2 = Mockito.mock(DiscoveryFilter.class); - - Class cls = DiscoveryFilter.class; - try (MockedStatic spiServiceUtilsMockedStatic = Mockito.mockStatic(SPIServiceUtils.class)) { - spiServiceUtilsMockedStatic.when(() -> SPIServiceUtils.getSortedService(cls)).thenReturn(Arrays.asList(f1, f2)); - - discoveryTree.loadFromSPI(cls); + @BeforeEach + public void before() { - MatcherAssert.assertThat(filters, Matchers.contains(f1, f2)); - } } - - @Test - public void sort() { - DiscoveryFilter f1 = Mockito.mock(DiscoveryFilter.class); - DiscoveryFilter f2 = Mockito.mock(DiscoveryFilter.class); - DiscoveryFilter f3 = Mockito.mock(DiscoveryFilter.class); - Mockito.when(f1.getOrder()).thenReturn(-1); - Mockito.when(f1.enabled()).thenReturn(true); - Mockito.when(f2.getOrder()).thenReturn(0); - Mockito.when(f2.enabled()).thenReturn(true); - Mockito.when(f3.getOrder()).thenReturn(0); - Mockito.when(f3.enabled()).thenReturn(false); - - discoveryTree.addFilter(f3); - discoveryTree.addFilter(f2); - discoveryTree.addFilter(f1); - discoveryTree.sort(); - - MatcherAssert.assertThat(filters, Matchers.contains(f1, f2)); + @AfterEach + public void tearDown() { } - @Test public void isMatch_existingNull() { + DiscoveryTree discoveryTree = new DiscoveryTree( + new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); Assertions.assertFalse(discoveryTree.isMatch(null, null)); } @Test public void isMatch_yes() { + DiscoveryTree discoveryTree = new DiscoveryTree(new DiscoveryManager(Collections.emptyList(), + List.of(new TelnetInstancePing()))); parent.cacheVersion(1); Assertions.assertTrue(discoveryTree.isMatch(new DiscoveryTreeNode().cacheVersion(1), parent)); } @Test public void isMatch_no() { + DiscoveryTree discoveryTree = new DiscoveryTree( + new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); parent.cacheVersion(0); Assertions.assertFalse(discoveryTree.isExpired(new DiscoveryTreeNode().cacheVersion(1), parent)); } @Test public void isExpired_existingNull() { + DiscoveryTree discoveryTree = new DiscoveryTree( + new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); Assertions.assertTrue(discoveryTree.isExpired(null, null)); } @Test public void isExpired_yes() { + DiscoveryTree discoveryTree = new DiscoveryTree( + new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); parent.cacheVersion(1); Assertions.assertTrue(discoveryTree.isExpired(new DiscoveryTreeNode().cacheVersion(0), parent)); } @Test public void isExpired_no() { + DiscoveryTree discoveryTree = new DiscoveryTree( + new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); parent.cacheVersion(0); Assertions.assertFalse(discoveryTree.isExpired(new DiscoveryTreeNode().cacheVersion(0), parent)); } @@ -157,38 +125,34 @@ public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode p @Test public void filterNormal() { + DiscoveryTree discoveryTree = new DiscoveryTree( + new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); parent.name("1.0.0-2.0.0"); - discoveryTree.addFilter(new DiscoveryFilterForTest("g1")); - discoveryTree.addFilter(new DiscoveryFilterForTest(null)); - discoveryTree.addFilter(new DiscoveryFilterForTest("g2")); - discoveryTree.addFilter(new DiscoveryFilterForTest(null)); - - result = discoveryTree.discovery(context, parent); + discoveryTree.setDiscoveryFilters(Arrays.asList(new DiscoveryFilterForTest("g1"), + new DiscoveryFilterForTest(null), new DiscoveryFilterForTest("g2"), + new DiscoveryFilterForTest(null))); + result = discoveryTree.discovery("app", "service", context, parent); Assertions.assertEquals("1.0.0-2.0.0/g1/g2", result.name()); } @Test public void easyDiscovery() { - InstanceCacheManager instanceCacheManager = Mockito.mock(InstanceCacheManager.class); - DiscoveryManager.INSTANCE = Mockito.spy(DiscoveryManager.INSTANCE); - Mockito.when(DiscoveryManager.INSTANCE.getInstanceCacheManager()).thenReturn(instanceCacheManager); - Mockito.when(instanceCacheManager.getOrCreateVersionedCache(null, null, null)).thenReturn(parent); - + DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); + DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); + Mockito.when(discoveryManager.getOrCreateVersionedCache("app", "svc")).thenReturn(parent); - result = discoveryTree.discovery(context, null, null, null); + result = discoveryTree.discovery(context, "app", "svc"); Assertions.assertEquals(parent.name(), result.name()); Assertions.assertEquals(parent.cacheVersion(), result.cacheVersion()); } @Test public void discovery_filterReturnNull() { - InstanceCacheManager instanceCacheManager = Mockito.mock(InstanceCacheManager.class); - DiscoveryManager.INSTANCE = Mockito.spy(DiscoveryManager.INSTANCE); - Mockito.when(DiscoveryManager.INSTANCE.getInstanceCacheManager()).thenReturn(instanceCacheManager); - Mockito.when(instanceCacheManager.getOrCreateVersionedCache(null, null, null)).thenReturn(parent); - + DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); + Mockito.when(discoveryManager.getOrCreateVersionedCache("app", "service")).thenReturn(parent); + DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); DiscoveryFilter filter = new DiscoveryFilter() { @Override public int getOrder() { @@ -200,10 +164,10 @@ public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode p return null; } }; - discoveryTree.addFilter(filter); + discoveryTree.setDiscoveryFilters(Arrays.asList(filter)); ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, - () -> result = discoveryTree.discovery(context, null, null, null)); + () -> result = discoveryTree.discovery(context, "app", "service")); Assertions.assertEquals(filter.getClass().getName() + " discovery return null.", exception.getMessage()); } @@ -211,7 +175,7 @@ public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode p public void filterRerun() { parent.name("1.0.0-2.0.0"); - discoveryTree.addFilter(new DiscoveryFilterForTest("g1") { + DiscoveryFilterForTest f1 = new DiscoveryFilterForTest("g1") { @Override public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { if (context.getContextParameter("step") == null) { @@ -222,8 +186,8 @@ public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode p return new DiscoveryTreeNode().name(groupName).data("second"); } - }); - discoveryTree.addFilter(new DiscoveryFilterForTest(null) { + }; + DiscoveryFilterForTest f2 = new DiscoveryFilterForTest(null) { @Override public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { if ("first".equals(parent.data())) { @@ -232,50 +196,84 @@ public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode p return new DiscoveryTreeNode().data(parent.data()); } - }); - - result = discoveryTree.discovery(context, parent); + }; + DiscoveryTree discoveryTree = new DiscoveryTree( + new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); + discoveryTree.setDiscoveryFilters(Arrays.asList(f1, f2)); + result = discoveryTree.discovery("app", "service", context, parent); Assertions.assertEquals("second", result.data()); } @Test - public void avoidConcurrentProblem() { - discoveryTree.setRoot(parent.cacheVersion(1)); - Assertions.assertTrue(parent.children().isEmpty()); - - discoveryTree.discovery(context, new VersionedCache().cacheVersion(0).name("input")); - Assertions.assertTrue(parent.children().isEmpty()); + @SuppressWarnings("unchecked") + public void test_multi_service_discovery_correct() { + DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); + DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); + DiscoveryContext discoveryContext = new DiscoveryContext(); + + List service1 = Arrays.asList("s11", "s12"); + Mockito.when(discoveryManager.getOrCreateVersionedCache("app", "service1")) + .thenReturn(new VersionedCache().name("0+").data(service1)); + DiscoveryTreeNode result = discoveryTree.discovery(discoveryContext, "app", "service1"); + Assertions.assertArrayEquals(service1.toArray(new String[0]), + ((List) result.data()).toArray(new String[0])); + + List service2 = Arrays.asList("s21", "s22"); + Mockito.when(discoveryManager.getOrCreateVersionedCache("app", "service2")) + .thenReturn(new VersionedCache().name("0+").data(service2)); + result = discoveryTree.discovery(discoveryContext, "app", "service2"); + Assertions.assertArrayEquals(service2.toArray(new String[0]), + ((List) result.data()).toArray(new String[0])); + + result = discoveryTree.discovery(discoveryContext, "app", "service1"); + Assertions.assertArrayEquals(service1.toArray(new String[0]), + ((List) result.data()).toArray(new String[0])); } @Test - public void getOrCreateRoot_match() { - discoveryTree.setRoot(parent); - - DiscoveryTreeNode root = discoveryTree.getOrCreateRoot(parent); - - Assertions.assertSame(parent, root); - } - - @Test - public void getOrCreateRoot_expired() { - discoveryTree.setRoot(parent); - - VersionedCache inputCache = new VersionedCache().cacheVersion(parent.cacheVersion() + 1); - DiscoveryTreeNode root = discoveryTree.getOrCreateRoot(inputCache); - - Assertions.assertEquals(inputCache.cacheVersion(), root.cacheVersion()); - Assertions.assertSame(discoveryTree.getRoot(), root); - } - - @Test - public void getOrCreateRoot_tempRoot() { - discoveryTree.setRoot(parent); - - VersionedCache inputCache = new VersionedCache().cacheVersion(parent.cacheVersion() - 1); - DiscoveryTreeNode root = discoveryTree.getOrCreateRoot(inputCache); + @SuppressWarnings("unchecked") + public void test_one_service_concurrent_correct() throws Exception { + DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); + DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); + DiscoveryContext discoveryContext = new DiscoveryContext(); + InstanceStatusDiscoveryFilter filter = new InstanceStatusDiscoveryFilter(); + DynamicProperties dynamicProperties = Mockito.mock(DynamicProperties.class); + Mockito.when(dynamicProperties.getBooleanProperty(Mockito.eq("servicecomb.loadbalance.filter.status.enabled"), + Mockito.any(), + Mockito.eq(true))) + .thenReturn(true); + filter.setDynamicProperties(dynamicProperties); + discoveryTree.setDiscoveryFilters(List.of(filter)); + + StatefulDiscoveryInstance instance1 = Mockito.mock(StatefulDiscoveryInstance.class); + StatefulDiscoveryInstance instance2 = Mockito.mock(StatefulDiscoveryInstance.class); + + VersionedCache expects0 = new VersionedCache().autoCacheVersion().name("0+") + .data(Arrays.asList(instance1, instance2)); + VersionedCache[] expects999 = new VersionedCache[999]; + for (int i = 0; i < 999; i++) { + expects999[i] = new VersionedCache().name("0+") + .data(Arrays.asList(instance1, instance2)).cacheVersion(i + 1); + } + Mockito.when(discoveryManager.getOrCreateVersionedCache("app", "service1")) + .thenReturn(expects0, expects999); + + CountDownLatch countDownLatch = new CountDownLatch(1000); + AtomicInteger success = new AtomicInteger(0); + for (int i = 0; i < 10; i++) { + new Thread(() -> { + for (int j = 0; j < 100; j++) { + DiscoveryTreeNode result = discoveryTree.discovery(discoveryContext, "app", "service1"); + if (((List) result.data()).size() == 2) { + success.getAndIncrement(); + } + countDownLatch.countDown(); + } + }).start(); + } - Assertions.assertEquals(inputCache.cacheVersion(), root.cacheVersion()); - Assertions.assertNotSame(discoveryTree.getRoot(), root); + countDownLatch.await(3000, TimeUnit.MILLISECONDS); + Assertions.assertEquals(1000, success.get()); } } diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestInstanceStatusDiscoveryFilter.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestInstanceStatusDiscoveryFilter.java new file mode 100644 index 00000000000..8e7307098b9 --- /dev/null +++ b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestInstanceStatusDiscoveryFilter.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.discovery; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.HistoryStatus; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.IsolationStatus; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.PingStatus; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestInstanceStatusDiscoveryFilter { + @Test + public void test_all_group_correct_init() { + InstanceStatusDiscoveryFilter filter = new InstanceStatusDiscoveryFilter(); + DiscoveryTreeNode parent = new DiscoveryTreeNode(); + List instances = new ArrayList<>(); + StatefulDiscoveryInstance instance1 = Mockito.mock(StatefulDiscoveryInstance.class); + Mockito.when(instance1.getHistoryStatus()).thenReturn(HistoryStatus.CURRENT); + Mockito.when(instance1.getStatus()).thenReturn(MicroserviceInstanceStatus.UP); + Mockito.when(instance1.getPingStatus()).thenReturn(PingStatus.OK); + Mockito.when(instance1.getIsolationStatus()).thenReturn(IsolationStatus.NORMAL); + StatefulDiscoveryInstance instance2 = Mockito.mock(StatefulDiscoveryInstance.class); + Mockito.when(instance2.getHistoryStatus()).thenReturn(HistoryStatus.CURRENT); + Mockito.when(instance2.getStatus()).thenReturn(MicroserviceInstanceStatus.UP); + Mockito.when(instance2.getPingStatus()).thenReturn(PingStatus.UNKNOWN); + Mockito.when(instance2.getIsolationStatus()).thenReturn(IsolationStatus.NORMAL); + StatefulDiscoveryInstance instance3 = Mockito.mock(StatefulDiscoveryInstance.class); + Mockito.when(instance3.getHistoryStatus()).thenReturn(HistoryStatus.HISTORY); + Mockito.when(instance3.getStatus()).thenReturn(MicroserviceInstanceStatus.UP); + Mockito.when(instance3.getPingStatus()).thenReturn(PingStatus.OK); + Mockito.when(instance3.getIsolationStatus()).thenReturn(IsolationStatus.NORMAL); + StatefulDiscoveryInstance instance4 = Mockito.mock(StatefulDiscoveryInstance.class); + Mockito.when(instance4.getHistoryStatus()).thenReturn(HistoryStatus.CURRENT); + Mockito.when(instance4.getStatus()).thenReturn(MicroserviceInstanceStatus.UP); + Mockito.when(instance4.getPingStatus()).thenReturn(PingStatus.FAIL); + Mockito.when(instance4.getIsolationStatus()).thenReturn(IsolationStatus.NORMAL); + instances.addAll(Arrays.asList(instance1, instance2, instance3, instance4)); + + parent.name("parent"); + parent.data(instances); + filter.init(new DiscoveryContext(), parent); + + List level0 = parent + .child(InstanceStatusDiscoveryFilter.GROUP_PREFIX + 1) + .data(); + Assertions.assertEquals(1, level0.size()); + List level1 = parent + .child(InstanceStatusDiscoveryFilter.GROUP_PREFIX + 2) + .data(); + Assertions.assertEquals(1, level1.size()); + Assertions.assertEquals(1, level1.size()); + List level2 = parent + .child(InstanceStatusDiscoveryFilter.GROUP_PREFIX + 3) + .data(); + Assertions.assertEquals(1, level2.size()); + List level3 = parent + .child(InstanceStatusDiscoveryFilter.GROUP_PREFIX + 4) + .data(); + Assertions.assertEquals(1, level3.size()); + + Assertions.assertEquals(null, parent + .child(InstanceStatusDiscoveryFilter.GROUP_PREFIX + 5)); + } +} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersion.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersion.java deleted file mode 100644 index 737cc2663d7..00000000000 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersion.java +++ /dev/null @@ -1,177 +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.servicecomb.registry.version; - -import org.apache.servicecomb.foundation.common.Version; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestVersion { - Version version; - - short s1 = 1; - - short s2 = 2; - - @Test - public void constructFromStringNormalOnlyMajor() { - version = new Version("1"); - Assertions.assertEquals("1.0.0.0", version.getVersion()); - Assertions.assertEquals(1, version.getMajor()); - Assertions.assertEquals(0, version.getMinor()); - Assertions.assertEquals(0, version.getPatch()); - } - - @Test - public void constructFromStringNormalOnlyMajorMinor() { - version = new Version("1.1"); - Assertions.assertEquals("1.1.0.0", version.getVersion()); - Assertions.assertEquals(1, version.getMajor()); - Assertions.assertEquals(1, version.getMinor()); - Assertions.assertEquals(0, version.getPatch()); - } - - @Test - public void constructFromStringOnlyMajorMinorPatch() { - version = new Version("1.1.1"); - Assertions.assertEquals("1.1.1.0", version.getVersion()); - Assertions.assertEquals(1, version.getMajor()); - Assertions.assertEquals(1, version.getMinor()); - Assertions.assertEquals(1, version.getPatch()); - Assertions.assertEquals(0, version.getBuild()); - } - - @Test - public void constructFromStringNormal() { - version = new Version("1.1.1.1"); - Assertions.assertEquals("1.1.1.1", version.getVersion()); - Assertions.assertEquals(1, version.getMajor()); - Assertions.assertEquals(1, version.getMinor()); - Assertions.assertEquals(1, version.getPatch()); - Assertions.assertEquals(1, version.getBuild()); - } - - @Test - public void constructFromStringInvalidNull() { - Assertions.assertThrows(NullPointerException.class, () -> version = new Version(null)); - } - - @Test - public void constructFromStringInvalidEmpty() { - IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> version = new Version("")); - Assertions.assertEquals("Invalid major \"\", version \"\".", exception.getMessage()); - Assertions.assertTrue(exception.getCause() instanceof NumberFormatException); - } - - @Test - public void constructFromStringInvalidMajorNegative() { - IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> version = new Version("-1")); - Assertions.assertEquals("major \"-1\" can not be negative, version \"-1\".", exception.getMessage()); - } - - @Test - public void constructFromStringInvalidMajorDot() { - IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> version = new Version("1.")); - Assertions.assertEquals("Invalid minor \"\", version \"1.\".", exception.getMessage()); - Assertions.assertTrue(exception.getCause() instanceof NumberFormatException); - } - - @Test - public void constructFromStringInvalidMinorNegative() { - IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> version = new Version("1.-1")); - Assertions.assertEquals("minor \"-1\" can not be negative, version \"1.-1\".", exception.getMessage()); - } - - @Test - public void constructFromStringInvalidMinorDot() { - IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> version = new Version("1.1.")); - Assertions.assertEquals("Invalid patch \"\", version \"1.1.\".", exception.getMessage()); - Assertions.assertTrue(exception.getCause() instanceof NumberFormatException); - } - - @Test - public void constructFromStringInvalidPatchNegative() { - IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> version = new Version("1.1.-1")); - Assertions.assertEquals("patch \"-1\" can not be negative, version \"1.1.-1\".", exception.getMessage()); - } - - @Test - public void constructFromStringInvalidPatchDot() { - IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> version = new Version("1.1.1.")); - Assertions.assertEquals("Invalid build \"\", version \"1.1.1.\".", exception.getMessage()); - Assertions.assertTrue(exception.getCause() instanceof NumberFormatException); - } - - @Test - public void constructFromStringInvalidBuildNegative() { - IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> version = new Version("1.1.1.-1")); - Assertions.assertEquals("build \"-1\" can not be negative, version \"1.1.1.-1\".", exception.getMessage()); - } - - @Test - public void constructFromStringInvalidTooManyPart() { - IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> version = new Version("1.1.1.1.")); - Assertions.assertEquals("Invalid version \"1.1.1.1.\".", exception.getMessage()); - } - - @Test - public void constructFromNumber() { - version = new Version(s1, s1, s1, s1); - Assertions.assertEquals("1.1.1.1", version.getVersion()); - Assertions.assertEquals(1, version.getMajor()); - Assertions.assertEquals(1, version.getMinor()); - Assertions.assertEquals(1, version.getPatch()); - Assertions.assertEquals(1, version.getPatch()); - } - - @Test - public void testToString() { - version = new Version(s1, s1, s1, s1); - Assertions.assertEquals("1.1.1.1", version.toString()); - } - - @Test - public void testHashCode() { - version = new Version(s1, s1, s1, s1); - Assertions.assertEquals(version.getVersion().hashCode(), version.hashCode()); - } - - @Test - public void testEquals() { - version = new Version(s1, s1, s1, s1); - - Assertions.assertEquals(version, version); - Assertions.assertEquals(version, new Version(s1, s1, s1, s1)); - Assertions.assertNotEquals(null, version); - } - - @Test - public void compareTo() { - version = new Version(s1, s1, s1, s1); - - Assertions.assertEquals(0, version.compareTo(version)); - Assertions.assertEquals(0, version.compareTo(new Version(s1, s1, s1, s1))); - - Assertions.assertEquals(-1, version.compareTo(new Version(s1, s1, s2, s1))); - Assertions.assertEquals(-1, version.compareTo(new Version(s1, s2, s1, s1))); - Assertions.assertEquals(-1, version.compareTo(new Version(s2, s1, s1, s1))); - - Assertions.assertEquals(1, version.compareTo(new Version((short) 0, - Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE))); - } -} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRule.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRule.java deleted file mode 100644 index a2a33824261..00000000000 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRule.java +++ /dev/null @@ -1,47 +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.servicecomb.registry.version; - -import org.apache.servicecomb.foundation.common.Version; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestVersionRule { - static class VersionRuleForTest extends VersionRule { - public VersionRuleForTest(String versionRule) { - super(versionRule); - } - - @Override - public boolean isMatch(Version version, Version latestVersion) { - return true; - } - } - - VersionRule versionRule = new VersionRuleForTest("abc"); - - @Test - public void isAccept() { - Assertions.assertTrue(versionRule.isAccept(null)); - } - - @Test - public void getVersionRule() { - Assertions.assertEquals("abc", versionRule.getVersionRule()); - } -} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleFixedParser.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleFixedParser.java deleted file mode 100644 index 2fec0b623c9..00000000000 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleFixedParser.java +++ /dev/null @@ -1,40 +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.servicecomb.registry.version; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestVersionRuleFixedParser { - VersionRule versionRule; - - VersionRuleParser parser = new VersionRuleFixedParser(); - - @Test - public void parseNormal() { - versionRule = parser.parse("1"); - Assertions.assertEquals("1.0.0.0", versionRule.getVersionRule()); - } - - @Test - public void isMatch() { - versionRule = parser.parse("1"); - Assertions.assertTrue(versionRule.isMatch(VersionConst.v1, null)); - Assertions.assertFalse(versionRule.isMatch(VersionConst.v2, null)); - } -} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleLatestParser.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleLatestParser.java deleted file mode 100644 index 2592fc3a12e..00000000000 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleLatestParser.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.servicecomb.registry.version; - -import org.apache.servicecomb.registry.definition.DefinitionConst; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestVersionRuleLatestParser { - VersionRuleParser parser = new VersionRuleLatestParser(); - - VersionRule versionRule = parser.parse(DefinitionConst.VERSION_RULE_LATEST); - - @Test - public void parseInvalue() { - Assertions.assertNull(parser.parse("")); - } - - @Test - public void parseNormal() { - Assertions.assertEquals(DefinitionConst.VERSION_RULE_LATEST, versionRule.getVersionRule()); - } - - @Test - public void isAccept() { - Assertions.assertTrue(versionRule.isAccept(null)); - } - - @Test - public void isMatch() { - Assertions.assertTrue(versionRule.isMatch(VersionConst.v1, VersionConst.v1)); - Assertions.assertFalse(versionRule.isMatch(VersionConst.v2, VersionConst.v1)); - } -} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleRangeParser.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleRangeParser.java deleted file mode 100644 index 605e31671c8..00000000000 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleRangeParser.java +++ /dev/null @@ -1,47 +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.servicecomb.registry.version; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestVersionRuleRangeParser { - VersionRuleParser parser = new VersionRuleRangeParser(); - - VersionRule versionRule = parser.parse("1-2"); - - @Test - public void parseInvalid() { - Assertions.assertNull(parser.parse("")); - Assertions.assertNull(parser.parse("-")); - Assertions.assertNull(parser.parse("1-")); - } - - @Test - public void parseNormal() { - Assertions.assertEquals("1.0.0.0-2.0.0.0", versionRule.getVersionRule()); - } - - @Test - public void isMatch() { - Assertions.assertFalse(versionRule.isMatch(VersionConst.v0, null)); - Assertions.assertTrue(versionRule.isMatch(VersionConst.v1, null)); - Assertions.assertTrue(versionRule.isMatch(VersionConst.v1Max, null)); - Assertions.assertFalse(versionRule.isMatch(VersionConst.v2, null)); - } -} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleStartFromParser.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleStartFromParser.java deleted file mode 100644 index c5f31ed11ca..00000000000 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleStartFromParser.java +++ /dev/null @@ -1,47 +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.servicecomb.registry.version; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestVersionRuleStartFromParser { - VersionRuleParser parser = new VersionRuleStartFromParser(); - - VersionRule versionRule = parser.parse("1+"); - - @Test - public void parseInvalid() { - Assertions.assertNull(parser.parse("")); - Assertions.assertNull(parser.parse("+")); - Assertions.assertNull(parser.parse("1+1")); - } - - @Test - public void parseNormal() { - Assertions.assertEquals("1.0.0.0+", versionRule.getVersionRule()); - } - - @Test - public void isMatch() { - Assertions.assertFalse(versionRule.isMatch(VersionConst.v0, null)); - Assertions.assertTrue(versionRule.isMatch(VersionConst.v1, null)); - Assertions.assertTrue(versionRule.isMatch(VersionConst.v1Max, null)); - Assertions.assertTrue(versionRule.isMatch(VersionConst.v2, null)); - } -} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleUtils.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleUtils.java deleted file mode 100644 index e51af7a0ff3..00000000000 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionRuleUtils.java +++ /dev/null @@ -1,63 +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.servicecomb.registry.version; - -import org.apache.servicecomb.registry.definition.DefinitionConst; -import org.apache.servicecomb.registry.version.VersionRuleFixedParser.FixedVersionRule; -import org.apache.servicecomb.registry.version.VersionRuleLatestParser.LatestVersionRule; -import org.apache.servicecomb.registry.version.VersionRuleRangeParser.RangeVersionRule; -import org.apache.servicecomb.registry.version.VersionRuleStartFromParser.StartFromVersionRule; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestVersionRuleUtils { - - @Test - public void fixed() { - VersionRule versionRule = VersionRuleUtils.getOrCreate("1"); - Assertions.assertTrue(versionRule instanceof FixedVersionRule); - Assertions.assertSame(versionRule, VersionRuleUtils.getOrCreate("1")); - } - - @Test - public void latest() { - VersionRule versionRule = VersionRuleUtils.getOrCreate(DefinitionConst.VERSION_RULE_LATEST); - Assertions.assertTrue(versionRule instanceof LatestVersionRule); - Assertions.assertSame(versionRule, VersionRuleUtils.getOrCreate(DefinitionConst.VERSION_RULE_LATEST)); - } - - @Test - public void range() { - VersionRule versionRule = VersionRuleUtils.getOrCreate("1-2"); - Assertions.assertTrue(versionRule instanceof RangeVersionRule); - Assertions.assertSame(versionRule, VersionRuleUtils.getOrCreate("1-2")); - } - - @Test - public void startFrom() { - VersionRule versionRule = VersionRuleUtils.getOrCreate("1+"); - Assertions.assertTrue(versionRule instanceof StartFromVersionRule); - Assertions.assertSame(versionRule, VersionRuleUtils.getOrCreate("1+")); - } - - @Test - public void invalid() { - IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> VersionRuleUtils.getOrCreate("")); - Assertions.assertEquals("Invalid major \"\", version \"\".", exception.getMessage()); - } -} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionUtils.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionUtils.java deleted file mode 100644 index 03eec63d9fa..00000000000 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/TestVersionUtils.java +++ /dev/null @@ -1,45 +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.servicecomb.registry.version; - -import org.apache.servicecomb.foundation.common.Version; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class TestVersionUtils { - @BeforeEach - public void setup() { - VersionUtils.clear(); - } - - @AfterEach - public void teardown() { - VersionUtils.clear(); - } - - @Test - public void getOrCreate() { - Version v = VersionUtils.getOrCreate("1.0.0"); - - Assertions.assertEquals("1.0.0.0", v.getVersion()); - Assertions.assertSame(v, VersionUtils.getOrCreate("1.0.0")); - } -} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/VersionConst.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/VersionConst.java deleted file mode 100644 index f8df0570e54..00000000000 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/version/VersionConst.java +++ /dev/null @@ -1,30 +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.servicecomb.registry.version; - -import org.apache.servicecomb.foundation.common.Version; - -public interface VersionConst { - Version v0 = new Version((short) 0, (short) 0, (short) 0, (short) 0); - - Version v1 = new Version("1"); - - Version v1Max = new Version((short) 1, Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE); - - Version v2 = new Version("2"); -} diff --git a/foundations/foundation-registry/src/test/resources/log4j2.xml b/foundations/foundation-registry/src/test/resources/log4j2.xml new file mode 100644 index 00000000000..3125ebd11e4 --- /dev/null +++ b/foundations/foundation-registry/src/test/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/foundations/foundation-spi/pom.xml b/foundations/foundation-spi/pom.xml index d5184a62f47..aa38214b3ee 100644 --- a/foundations/foundation-spi/pom.xml +++ b/foundations/foundation-spi/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb foundations - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT foundation-spi Java Chassis::Foundations::SSL @@ -41,19 +41,4 @@ test - diff --git a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderLoader.java b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderLoader.java index 39e0bfa1a5d..668e77faa38 100644 --- a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderLoader.java +++ b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderLoader.java @@ -29,6 +29,10 @@ public class AuthHeaderLoader { private static final AuthHeaderLoader INSTANCE = new AuthHeaderLoader(); + private AuthHeaderLoader() { + + } + public static AuthHeaderLoader getInstance() { return INSTANCE; } diff --git a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderProvider.java b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderProvider.java index 313c19ee5ba..0e036e21a58 100644 --- a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderProvider.java +++ b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderProvider.java @@ -21,11 +21,21 @@ import java.util.Map; public interface AuthHeaderProvider { - default Map authHeaders() { + /** + * Obtain RBAC authentication request header, host is the key of cache + * + * @param host engine address ip + * @return auth headers + */ + default Map authHeaders(String host) { return new HashMap<>(0); } default Map getSignAuthHeaders(SignRequest request) { - return authHeaders(); + String host = ""; + if (request != null && request.getEndpoint() != null) { + host = request.getEndpoint().getHost(); + } + return authHeaders(host); } } diff --git a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIOrder.java b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIOrder.java index b9fc016ee17..adaf18de073 100644 --- a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIOrder.java +++ b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIOrder.java @@ -17,7 +17,9 @@ package org.apache.servicecomb.foundation.common.utils; -public interface SPIOrder { +import org.springframework.core.Ordered; + +public interface SPIOrder extends Ordered { default int getOrder() { return 0; } diff --git a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIServiceUtils.java b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIServiceUtils.java index ea8a0bc12ea..abd2dcab32e 100644 --- a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIServiceUtils.java +++ b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIServiceUtils.java @@ -76,11 +76,12 @@ public static List loadSortedService(Class serviceType) { .map(Entry::getValue) .collect(Collectors.toList()); - LOGGER.info("Found SPI service {}, count={}.", serviceType.getName(), services.size()); + StringBuilder info = new StringBuilder(); for (int idx = 0; idx < services.size(); idx++) { T service = services.get(idx); - LOGGER.info(" {}. {}.", idx, service.getClass().getName()); + info.append("{").append(idx).append(",").append(service.getClass().getSimpleName()).append("}"); } + LOGGER.info("Found SPI service {}, services={}.", serviceType.getSimpleName(), info); return services; } diff --git a/foundations/foundation-spi/src/test/resources/META-INF/services/org.apache.servicecomb.foundation.common.utils.SPIServiceDef b/foundations/foundation-spi/src/test/resources/META-INF/services/org.apache.servicecomb.foundation.common.utils.SPIServiceDef index eebf462d01b..7d8b6d492a4 100644 --- a/foundations/foundation-spi/src/test/resources/META-INF/services/org.apache.servicecomb.foundation.common.utils.SPIServiceDef +++ b/foundations/foundation-spi/src/test/resources/META-INF/services/org.apache.servicecomb.foundation.common.utils.SPIServiceDef @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.foundation.common.utils.SPIServiceDefImpl \ No newline at end of file +org.apache.servicecomb.foundation.common.utils.SPIServiceDefImpl diff --git a/foundations/foundation-ssl/pom.xml b/foundations/foundation-ssl/pom.xml index ef52a56ab35..3cf7cdcb485 100644 --- a/foundations/foundation-ssl/pom.xml +++ b/foundations/foundation-ssl/pom.xml @@ -21,19 +21,19 @@ org.apache.servicecomb foundations - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT foundation-ssl Java Chassis::Foundations::SSL - com.netflix.archaius - archaius-core + org.springframework + spring-context - commons-configuration - commons-configuration + org.apache.commons + commons-lang3 org.slf4j diff --git a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLManager.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLManager.java index aeb54ffab7f..b7136f7752e 100644 --- a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLManager.java +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLManager.java @@ -34,7 +34,8 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509ExtendedTrustManager; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; + /** * 根据传递的SSLOption构造SSL上下文。请参考JSSE获取相关API的层次参考。 @@ -170,7 +171,7 @@ public static SSLServerSocket createSSLServerSocket(SSLOption option, setClientAuth(option, socket); return socket; } catch (UnknownHostException e) { - throw new IllegalArgumentException("unkown host"); + throw new IllegalArgumentException("unknown host"); } catch (IOException e) { throw new IllegalArgumentException("unable create socket"); } @@ -184,7 +185,7 @@ public static SSLSocket createSSLSocket(SSLOption option, SSLCustom custom) { (SSLSocket) factory.createSocket(); socket.setEnabledProtocols(option.getProtocols().split(",")); String[] supported = socket.getSupportedCipherSuites(); - String[] enabled = option.getCiphers().split(","); + String[] enabled = option.getCiphers().split("\\s*,\\s*"); socket.setEnabledCipherSuites(getEnabledCiphers(supported, enabled)); return socket; } catch (UnknownHostException e) { diff --git a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOption.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOption.java index bbc7e0d9d7d..19160bb3927 100644 --- a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOption.java +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOption.java @@ -25,11 +25,9 @@ import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; -import java.util.List; import java.util.Properties; -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.DynamicPropertyFactory; +import org.springframework.core.env.Environment; /** * SSL配置选项。 @@ -49,7 +47,7 @@ public final class SSLOption { DEFAULT_OPTION.setCheckCNHost(false); DEFAULT_OPTION.setCheckCNWhite(false); DEFAULT_OPTION.setCheckCNWhiteFile("white.list"); - DEFAULT_OPTION.setAllowRenegociate(true); + DEFAULT_OPTION.setAllowRenegotiate(true); DEFAULT_OPTION.setStorePath("internal"); DEFAULT_OPTION.setTrustStore("trust.jks"); DEFAULT_OPTION.setTrustStoreType("JKS"); @@ -74,7 +72,7 @@ public final class SSLOption { private String checkCNWhiteFile; - private boolean allowRenegociate; + private boolean allowRenegotiate; private String clientAuth; @@ -128,8 +126,8 @@ public void setCheckCNWhiteFile(String checkCNWhiteFile) { this.checkCNWhiteFile = checkCNWhiteFile; } - public void setAllowRenegociate(boolean allowRenegociate) { - this.allowRenegociate = allowRenegociate; + public void setAllowRenegotiate(boolean allowRenegotiate) { + this.allowRenegotiate = allowRenegotiate; } public void setStorePath(String storePath) { @@ -188,8 +186,8 @@ public String getCheckCNWhiteFile() { return checkCNWhiteFile; } - public boolean isAllowRenegociate() { - return allowRenegociate; + public boolean isAllowRenegotiate() { + return allowRenegotiate; } public String getStorePath() { @@ -253,30 +251,11 @@ public static SSLOption build(InputStream inputStream) { return option; } - private static String listToString(Object[] lists) { - StringBuilder sb = new StringBuilder(); - sb.append(lists[0]); - for (int i = 1; i < lists.length; i++) { - sb.append(","); - sb.append(lists[i]); - } - return sb.toString(); - } - - public static String getStringProperty(ConcurrentCompositeConfiguration configSource, String defaultValue, + public static String getStringProperty(Environment environment, String defaultValue, String... keys) { String property = null; for (String key : keys) { - if (configSource != null) { - Object v = configSource.getProperty(key); - if (v instanceof List) { - property = listToString(((List) v).toArray()); - } else { - property = (String) configSource.getProperty(key); - } - } else { - property = DynamicPropertyFactory.getInstance().getStringProperty(key, null).get(); - } + property = environment.getProperty(key); if (property != null) { break; } @@ -289,17 +268,11 @@ public static String getStringProperty(ConcurrentCompositeConfiguration configSo } } - private static boolean getBooleanProperty(ConcurrentCompositeConfiguration configSource, boolean defaultValue, + private static boolean getBooleanProperty(Environment environment, boolean defaultValue, String... keys) { String property = null; for (String key : keys) { - if (configSource != null) { - if (configSource.getProperty(key) != null) { - return configSource.getBoolean(key); - } - } else { - property = DynamicPropertyFactory.getInstance().getStringProperty(key, null).get(); - } + property = environment.getProperty(key); if (property != null) { break; } @@ -312,83 +285,79 @@ private static boolean getBooleanProperty(ConcurrentCompositeConfiguration confi } } - public static SSLOption buildFromYaml(String tag, ConcurrentCompositeConfiguration configSource) { + public static SSLOption build(String tag, Environment environment) { SSLOption option = new SSLOption(); - option.engine = getStringProperty(configSource, + option.engine = getStringProperty(environment, DEFAULT_OPTION.getEngine(), "ssl." + tag + ".engine", "ssl.engine"); option.protocols = - getStringProperty(configSource, + getStringProperty(environment, DEFAULT_OPTION.getProtocols(), "ssl." + tag + ".protocols", "ssl.protocols"); option.ciphers = - getStringProperty(configSource, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers"); + getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers"); option.authPeer = - getBooleanProperty(configSource, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer"); + getBooleanProperty(environment, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer"); option.checkCNHost = - getBooleanProperty(configSource, + getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNHost(), "ssl." + tag + ".checkCN.host", "ssl.checkCN.host"); option.checkCNWhite = - getBooleanProperty(configSource, + getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNWhite(), "ssl." + tag + ".checkCN.white", "ssl.checkCN.white"); - option.checkCNWhiteFile = getStringProperty(configSource, + option.checkCNWhiteFile = getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".checkCN.white.file", "ssl.checkCN.white.file"); - option.allowRenegociate = getBooleanProperty(configSource, - DEFAULT_OPTION.isAllowRenegociate(), - "ssl." + tag + ".allowRenegociate", - "ssl.allowRenegociate"); + option.allowRenegotiate = getBooleanProperty(environment, + DEFAULT_OPTION.isAllowRenegotiate(), + "ssl." + tag + ".allowRenegotiate", + "ssl.allowRenegotiate"); option.storePath = - getStringProperty(configSource, + getStringProperty(environment, DEFAULT_OPTION.getStorePath(), "ssl." + tag + ".storePath", "ssl.storePath"); option.clientAuth = - getStringProperty(configSource, + getStringProperty(environment, DEFAULT_OPTION.getClientAuth(), "ssl." + tag + ".storePath", "ssl.clientAuth"); option.trustStore = - getStringProperty(configSource, + getStringProperty(environment, DEFAULT_OPTION.getTrustStore(), "ssl." + tag + ".trustStore", "ssl.trustStore"); - option.trustStoreType = getStringProperty(configSource, + option.trustStoreType = getStringProperty(environment, DEFAULT_OPTION.getTrustStoreType(), "ssl." + tag + ".trustStoreType", "ssl.trustStoreType"); - option.trustStoreValue = getStringProperty(configSource, + option.trustStoreValue = getStringProperty(environment, DEFAULT_OPTION.getTrustStoreValue(), "ssl." + tag + ".trustStoreValue", "ssl.trustStoreValue"); option.keyStore = - getStringProperty(configSource, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore"); + getStringProperty(environment, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore"); option.keyStoreType = - getStringProperty(configSource, + getStringProperty(environment, DEFAULT_OPTION.getKeyStoreType(), "ssl." + tag + ".keyStoreType", "ssl.keyStoreType"); - option.keyStoreValue = getStringProperty(configSource, + option.keyStoreValue = getStringProperty(environment, DEFAULT_OPTION.getKeyStoreValue(), "ssl." + tag + ".keyStoreValue", "ssl.keyStoreValue"); - option.crl = getStringProperty(configSource, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl"); + option.crl = getStringProperty(environment, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl"); option.sslCustomClass = - getStringProperty(configSource, null, "ssl." + tag + ".sslCustomClass", "ssl.sslCustomClass"); + getStringProperty(environment, null, "ssl." + tag + ".sslCustomClass", "ssl.sslCustomClass"); return option; } - public static SSLOption buildFromYaml(String tag) { - return buildFromYaml(tag, null); - } - private void fromProperty(Properties props) { this.protocols = propString(props, "ssl.protocols"); this.ciphers = propString(props, "ssl.ciphers"); @@ -396,7 +365,7 @@ private void fromProperty(Properties props) { this.checkCNHost = propBoolean(props, "ssl.checkCN.host"); this.checkCNWhite = propBoolean(props, "ssl.checkCN.white"); this.checkCNWhiteFile = propString(props, "ssl.checkCN.white.file"); - this.allowRenegociate = propBoolean(props, "ssl.allowRenegociate"); + this.allowRenegotiate = propBoolean(props, "ssl.allowRenegotiate"); this.storePath = propString(props, "ssl.storePath"); this.clientAuth = propString(props, "ssl.clientAuth", false); this.trustStore = propString(props, "ssl.trustStore"); diff --git a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOptionFactory.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOptionFactory.java index 4abe85ca61b..49cdb3ab15b 100644 --- a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOptionFactory.java +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOptionFactory.java @@ -17,11 +17,11 @@ package org.apache.servicecomb.foundation.ssl; -import com.netflix.config.ConcurrentCompositeConfiguration; +import org.springframework.core.env.Environment; public interface SSLOptionFactory { - static SSLOptionFactory createSSLOptionFactory(String tag, ConcurrentCompositeConfiguration configSource) { - String name = SSLOption.getStringProperty(configSource, + static SSLOptionFactory createSSLOptionFactory(String tag, Environment environment) { + String name = SSLOption.getStringProperty(environment, null, "ssl." + tag + ".sslOptionFactory", "ssl.sslOptionFactory"); diff --git a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustManagerExt.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustManagerExt.java index 1177089097a..597894571c5 100644 --- a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustManagerExt.java +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustManagerExt.java @@ -189,7 +189,7 @@ private void checkCNHost(X509Certificate[] chain, String ip) throws CertificateE } } } catch (SocketException e) { - throw new CertificateException("Get local adrress fail."); + throw new CertificateException("Get local address fail."); } } else if (cnValid(cns, ipTmp)) { return; diff --git a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtilTest.java b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtilTest.java index f7d046f6562..54955139087 100644 --- a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtilTest.java +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtilTest.java @@ -26,13 +26,12 @@ import java.security.cert.CertificateFactory; import java.util.Collection; -import org.apache.commons.lang3.SystemUtils; import org.junit.Test; import org.junit.jupiter.api.Assertions; -import org.mockito.Mockito; import mockit.Mock; import mockit.MockUp; +import org.mockito.Mockito; public class KeyStoreUtilTest { final String strFilePath = Thread.currentThread().getContextClassLoader().getResource("").getPath(); @@ -78,14 +77,9 @@ public void testCreateKeyManagersException() { try { KeyStoreUtil.createKeyManagers(keystore, storeKeyValue); } catch (IllegalArgumentException e) { - if (SystemUtils.JAVA_SPECIFICATION_VERSION.startsWith("17")) { Assertions.assertEquals("Bad key store.Get Key failed:" + " Cannot read the array length because \"password\" is null", e.getMessage()); - } else { - Assertions.assertEquals("Bad key store.Get Key failed: null", - e.getMessage()); - } } } diff --git a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLOptionTest.java b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLOptionTest.java index 67ae3828499..ae91fcbb697 100644 --- a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLOptionTest.java +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLOptionTest.java @@ -24,32 +24,38 @@ import java.net.URL; import java.util.Properties; -import org.apache.commons.configuration.SystemConfiguration; -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.config.archaius.sources.ConfigSourceMaker; -import org.junit.AfterClass; +import org.junit.Before; import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.ConcurrentMapConfiguration; -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicConfiguration; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.FixedDelayPollingScheduler; - -import mockit.Deencapsulation; import mockit.Mock; import mockit.MockUp; -import org.junit.jupiter.api.Assertions; public class SSLOptionTest { private static final String DIR = Thread.currentThread().getContextClassLoader().getResource("").getPath(); - @AfterClass - public static void tearDown() throws Exception { - Deencapsulation.setField(ConfigurationManager.class, "instance", null); - Deencapsulation.setField(ConfigurationManager.class, "customConfigurationInstalled", false); - Deencapsulation.setField(DynamicPropertyFactory.class, "config", null); + Environment environment = Mockito.mock(Environment.class); + + @Before + public void setUp() throws Exception { + Mockito.when(environment.getProperty("ssl.protocols")).thenReturn("TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello"); + Mockito.when(environment.getProperty("ssl.ciphers")).thenReturn( + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"); + Mockito.when(environment.getProperty("ssl.authPeer")).thenReturn("true"); + Mockito.when(environment.getProperty("ssl.checkCN.host")).thenReturn("true"); + Mockito.when(environment.getProperty("ssl.checkCN.white")).thenReturn("true"); + Mockito.when(environment.getProperty("ssl.checkCN.white.file")).thenReturn("white.list"); + Mockito.when(environment.getProperty("ssl.allowRenegotiate")).thenReturn("false"); + Mockito.when(environment.getProperty("ssl.storePath")).thenReturn("internal"); + Mockito.when(environment.getProperty("ssl.trustStore")).thenReturn("trust.jks"); + Mockito.when(environment.getProperty("ssl.trustStoreType")).thenReturn("JKS"); + Mockito.when(environment.getProperty("ssl.trustStoreValue")).thenReturn("Changeme_123"); + Mockito.when(environment.getProperty("ssl.keyStore")).thenReturn("server.p12"); + Mockito.when(environment.getProperty("ssl.keyStoreType")).thenReturn("PKCS12"); + Mockito.when(environment.getProperty("ssl.keyStoreValue")).thenReturn("Changeme_123"); + Mockito.when(environment.getProperty("ssl.crl")).thenReturn("revoke.crl"); } @Test @@ -85,9 +91,9 @@ public void testSSLOption() { option.setCheckCNWhiteFile(checkCNWhiteFile); Assertions.assertEquals("white.list", checkCNWhiteFile); - boolean allowRenegociate = option.isAllowRenegociate(); - option.setAllowRenegociate(allowRenegociate); - Assertions.assertFalse(allowRenegociate); + boolean allowRenegotiate = option.isAllowRenegotiate(); + option.setAllowRenegotiate(allowRenegotiate); + Assertions.assertFalse(allowRenegotiate); String storePath = option.getStorePath(); option.setStorePath(storePath); @@ -124,21 +130,8 @@ public void testSSLOption() { @Test public void testSSLOptionYaml() { - // configuration from yaml files: default microservice.yaml - DynamicConfiguration configFromYamlFile = - new DynamicConfiguration(ConfigSourceMaker.yamlConfigSource(), new FixedDelayPollingScheduler()); - // configuration from system properties - ConcurrentMapConfiguration configFromSystemProperties = - new ConcurrentMapConfiguration(new SystemConfiguration()); - - // create a hierarchy of configuration that makes - // 1) dynamic configuration source override system properties - ConcurrentCompositeConfiguration finalConfig = new ConcurrentCompositeConfiguration(); - finalConfig.addConfiguration(configFromSystemProperties, "systemEnvConfig"); - finalConfig.addConfiguration(configFromYamlFile, "configFromYamlFile"); - ConfigurationManager.install(finalConfig); - - SSLOption option = SSLOption.buildFromYaml("server"); + Mockito.when(environment.getProperty("ssl.server.sslCustomClass")).thenReturn("wwrong"); + SSLOption option = SSLOption.build("server", environment); String protocols = option.getProtocols(); option.setProtocols(protocols); @@ -146,10 +139,7 @@ public void testSSLOptionYaml() { String ciphers = option.getCiphers(); option.setCiphers(ciphers); - Assertions.assertEquals( - "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SH" - + - "A,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA", + Assertions.assertEquals("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", ciphers); boolean authPeer = option.isAuthPeer(); @@ -168,9 +158,9 @@ public void testSSLOptionYaml() { option.setCheckCNWhiteFile(checkCNWhiteFile); Assertions.assertEquals("white.list", checkCNWhiteFile); - boolean allowRenegociate = option.isAllowRenegociate(); - option.setAllowRenegociate(allowRenegociate); - Assertions.assertFalse(allowRenegociate); + boolean allowRenegotiate = option.isAllowRenegotiate(); + option.setAllowRenegotiate(allowRenegotiate); + Assertions.assertFalse(allowRenegotiate); String storePath = option.getStorePath(); option.setStorePath(storePath); @@ -212,10 +202,9 @@ public void testSSLOptionYaml() { @Test public void testSSLOptionYamlOption2() throws Exception { - System.setProperty("ssl.protocols", "TLSv1.2"); - ConcurrentCompositeConfiguration finalConfig = ConfigUtil.createLocalConfig(); + Mockito.when(environment.getProperty("ssl.protocols")).thenReturn("TLSv1.2"); - SSLOption option = SSLOption.buildFromYaml("server", finalConfig); + SSLOption option = SSLOption.build("server", environment); String protocols = option.getProtocols(); option.setProtocols(protocols); @@ -224,11 +213,10 @@ public void testSSLOptionYamlOption2() throws Exception { } @Test - public void testSSLOptionYamlOptionWithProperyFalse() throws Exception { - System.setProperty("ssl.authPeer", "false"); - ConcurrentCompositeConfiguration finalConfig = ConfigUtil.createLocalConfig(); + public void testSSLOptionYamlOptionWithPropertyFalse() throws Exception { + Mockito.when(environment.getProperty("ssl.authPeer")).thenReturn("false"); - SSLOption option = SSLOption.buildFromYaml("server", finalConfig); + SSLOption option = SSLOption.build("server", environment); boolean authPeer = option.isAuthPeer(); option.setAuthPeer(authPeer); @@ -237,11 +225,8 @@ public void testSSLOptionYamlOptionWithProperyFalse() throws Exception { } @Test - public void testSSLOptionYamlOptionWithProperyTrue() throws Exception { - System.setProperty("ssl.authPeer", "true"); - ConcurrentCompositeConfiguration finalConfig = ConfigUtil.createLocalConfig(); - - SSLOption option = SSLOption.buildFromYaml("server", finalConfig); + public void testSSLOptionYamlOptionWithPropertyTrue() throws Exception { + SSLOption option = SSLOption.build("server", environment); boolean authPeer = option.isAuthPeer(); option.setAuthPeer(authPeer); @@ -251,9 +236,7 @@ public void testSSLOptionYamlOptionWithProperyTrue() throws Exception { @Test public void testSSLOptionYamlOption() throws Exception { - ConcurrentCompositeConfiguration finalConfig = ConfigUtil.createLocalConfig(); - - SSLOption option = SSLOption.buildFromYaml("server", finalConfig); + SSLOption option = SSLOption.build("server", environment); String protocols = option.getProtocols(); option.setProtocols(protocols); @@ -262,9 +245,7 @@ public void testSSLOptionYamlOption() throws Exception { String ciphers = option.getCiphers(); option.setCiphers(ciphers); Assertions.assertEquals( - "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SH" - + - "A,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", ciphers); boolean authPeer = option.isAuthPeer(); @@ -283,9 +264,9 @@ public void testSSLOptionYamlOption() throws Exception { option.setCheckCNWhiteFile(checkCNWhiteFile); Assertions.assertEquals("white.list", checkCNWhiteFile); - boolean allowRenegociate = option.isAllowRenegociate(); - option.setAllowRenegociate(allowRenegociate); - Assertions.assertFalse(allowRenegociate); + boolean allowRenegotiate = option.isAllowRenegotiate(); + option.setAllowRenegotiate(allowRenegotiate); + Assertions.assertFalse(allowRenegotiate); String storePath = option.getStorePath(); option.setStorePath(storePath); diff --git a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLOptionFactory.java b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLOptionFactory.java index 3ee54ccab47..1d3b1b8b229 100644 --- a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLOptionFactory.java +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLOptionFactory.java @@ -19,8 +19,8 @@ import org.junit.Test; import org.junit.jupiter.api.Assertions; - -import com.netflix.config.ConcurrentCompositeConfiguration; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; import mockit.Expectations; import mockit.Mock; @@ -28,10 +28,11 @@ import mockit.Mocked; public class TestSSLOptionFactory { + Environment environment = Mockito.mock(Environment.class); @Test public void testSSLOptionFactory() { - SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory("cc", null); + SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory("cc", environment); Assertions.assertNull(factory); } @@ -39,26 +40,26 @@ public void testSSLOptionFactory() { public void testSSLOptionFactoryWrong(@Mocked SSLOption option) { new Expectations() { { - SSLOption.getStringProperty((ConcurrentCompositeConfiguration) any, anyString, (String[]) any); + SSLOption.getStringProperty((Environment) any, anyString, (String[]) any); result = "wrong"; } }; IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, - () -> SSLOptionFactory.createSSLOptionFactory("cc", null)); + () -> SSLOptionFactory.createSSLOptionFactory("cc", null)); Assertions.assertEquals("Failed to create SSLOptionFactory.", exception.getMessage()); } @Test - public void testSSLOptionFactoryCorrent() { + public void testSSLOptionFactoryCurrent() { new MockUp() { @Mock - public String getStringProperty(ConcurrentCompositeConfiguration configSource, String defaultValue, + public String getStringProperty(Environment environment, String defaultValue, String... keys) { return "org.apache.servicecomb.foundation.ssl.MyOptionFactory"; } }; - SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory("cc", null); + SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory("cc", environment); Assertions.assertEquals(factory.createSSLOption().getProtocols(), "TLSv1.2"); } } diff --git a/foundations/foundation-ssl/src/test/resources/client.ssl.properties b/foundations/foundation-ssl/src/test/resources/client.ssl.properties index 4d25cd495bb..87808468196 100644 --- a/foundations/foundation-ssl/src/test/resources/client.ssl.properties +++ b/foundations/foundation-ssl/src/test/resources/client.ssl.properties @@ -17,12 +17,13 @@ #########SSL options ssl.protocols=TLSv1.3,TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello -ssl.ciphers=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA +## test with extra blank +ssl.ciphers=TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA ssl.authPeer=true ssl.checkCN.host=false ssl.checkCN.white=true ssl.checkCN.white.file=white.list -ssl.allowRenegociate=false +ssl.allowRenegotiate=false #########certificates config ssl.storePath=internal diff --git a/foundations/foundation-ssl/src/test/resources/microservice.yaml b/foundations/foundation-ssl/src/test/resources/microservice.yaml index 41ca3326e51..482c8e48ea7 100644 --- a/foundations/foundation-ssl/src/test/resources/microservice.yaml +++ b/foundations/foundation-ssl/src/test/resources/microservice.yaml @@ -23,7 +23,7 @@ ssl: checkCN.host: true checkCN.white: true checkCN.white.file: white.list - allowRenegociate: false + allowRenegotiate: false storePath: internal trustStore: trust.jks trustStoreType: JKS diff --git a/foundations/foundation-ssl/src/test/resources/server.ssl.properties b/foundations/foundation-ssl/src/test/resources/server.ssl.properties index aec677c2387..d2a8180ab51 100644 --- a/foundations/foundation-ssl/src/test/resources/server.ssl.properties +++ b/foundations/foundation-ssl/src/test/resources/server.ssl.properties @@ -22,7 +22,7 @@ ssl.authPeer=true ssl.checkCN.host=true ssl.checkCN.white=true ssl.checkCN.white.file=white.list -ssl.allowRenegociate=false +ssl.allowRenegotiate=false #########certificates config ssl.storePath=internal diff --git a/foundations/foundation-ssl/src/test/resources/server.ssl.resource.properties b/foundations/foundation-ssl/src/test/resources/server.ssl.resource.properties index f1327eeca56..01f7380ba71 100644 --- a/foundations/foundation-ssl/src/test/resources/server.ssl.resource.properties +++ b/foundations/foundation-ssl/src/test/resources/server.ssl.resource.properties @@ -22,7 +22,7 @@ ssl.authPeer=true ssl.checkCN.host=true ssl.checkCN.white=true ssl.checkCN.white.file=white.list -ssl.allowRenegociate=false +ssl.allowRenegotiate=false #########certificates config ssl.storePath=internal diff --git a/foundations/foundation-ssl/src/test/resources/ssl/server.cer b/foundations/foundation-ssl/src/test/resources/ssl/server.cer index 79a62b21dea..fa0810bf80f 100644 --- a/foundations/foundation-ssl/src/test/resources/ssl/server.cer +++ b/foundations/foundation-ssl/src/test/resources/ssl/server.cer @@ -50,18 +50,18 @@ Certificate: 53:08:33 Exponent: 65537 (0x10001) X509v3 extensions: - X509v3 Basic Constraints: + X509v3 Basic Constraints: CA:FALSE X509v3 Key Usage: critical Digital Signature, Non Repudiation, Key Encipherment - X509v3 Subject Key Identifier: + X509v3 Subject Key Identifier: 27:15:69:5B:D5:13:60:C4:5C:62:54:75:55:A1:1D:4D:57:27:03:1F - X509v3 Authority Key Identifier: + X509v3 Authority Key Identifier: keyid:D5:4E:82:08:70:16:63:3D:DF:87:25:0B:7D:2E:EB:83:21:6A:BA:5B DirName:/C=CN/ST=GuangDong/L=ShenZhen/O=Huawei Technologies Co., Ltd/OU=OSS & Service Tools Dept/CN=OSS3.0 CA serial:7D:FD:C5:D8:70:DA:6E:0F:00:00:00:00:00:00:00:00 - X509v3 Subject Alternative Name: + X509v3 Subject Alternative Name: DNS:10.57.65.225, IP Address:10.57.65.225 Signature Algorithm: sha256WithRSAEncryption 38:1b:40:c9:3c:7f:63:a0:2d:64:59:2b:d9:4a:4b:db:c4:70: diff --git a/foundations/foundation-ssl/src/test/resources/ssl/trust.cer b/foundations/foundation-ssl/src/test/resources/ssl/trust.cer index a41bd779130..f010f4bf058 100644 --- a/foundations/foundation-ssl/src/test/resources/ssl/trust.cer +++ b/foundations/foundation-ssl/src/test/resources/ssl/trust.cer @@ -50,18 +50,18 @@ Certificate: 05:60:4b Exponent: 65537 (0x10001) X509v3 extensions: - X509v3 Basic Constraints: + X509v3 Basic Constraints: CA:TRUE X509v3 Key Usage: critical Certificate Sign, CRL Sign - X509v3 Subject Key Identifier: + X509v3 Subject Key Identifier: D5:4E:82:08:70:16:63:3D:DF:87:25:0B:7D:2E:EB:83:21:6A:BA:5B - X509v3 Authority Key Identifier: + X509v3 Authority Key Identifier: keyid:D5:4E:82:08:70:16:63:3D:DF:87:25:0B:7D:2E:EB:83:21:6A:BA:5B DirName:/C=CN/ST=GuangDong/L=ShenZhen/O=Huawei Technologies Co., Ltd/OU=OSS & Service Tools Dept/CN=OSS3.0 CA serial:7D:FD:C5:D8:70:DA:6E:0F:00:00:00:00:00:00:00:00 - X509v3 Subject Alternative Name: + X509v3 Subject Alternative Name: email:oss3ca@huawei.com Signature Algorithm: sha256WithRSAEncryption 8a:52:5f:99:d0:7b:a0:8c:2d:e8:6c:ff:7a:bc:89:64:03:d4: diff --git a/foundations/foundation-ssl/src/test/resources/ssl/white.list b/foundations/foundation-ssl/src/test/resources/ssl/white.list index fe1d44d8e85..5719754cff9 100644 --- a/foundations/foundation-ssl/src/test/resources/ssl/white.list +++ b/foundations/foundation-ssl/src/test/resources/ssl/white.list @@ -1,4 +1,4 @@ 127.0.0.1 10.67.190.60 10.67.147.115 -10.57.65.225 \ No newline at end of file +10.57.65.225 diff --git a/foundations/foundation-test-scaffolding/pom.xml b/foundations/foundation-test-scaffolding/pom.xml index c96e8898cb7..f3feba62148 100644 --- a/foundations/foundation-test-scaffolding/pom.xml +++ b/foundations/foundation-test-scaffolding/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb foundations - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT foundation-test-scaffolding Java Chassis::Foundations::Test Scaffolding @@ -31,15 +31,6 @@ org.springframework spring-context - - com.netflix.archaius - archaius-core - - - commons-configuration - commons-configuration - compile - io.vertx vertx-web @@ -50,8 +41,8 @@ provided - com.netflix.spectator - spectator-reg-servo + com.google.guava + guava org.apache.logging.log4j @@ -59,6 +50,12 @@ provided true + + org.apache.logging.log4j + log4j-api + provided + true + com.fasterxml.jackson.core jackson-annotations @@ -71,6 +68,11 @@ org.springframework spring-aspects + + io.swagger.core.v3 + swagger-core-jakarta + provided + org.assertj assertj-core diff --git a/foundations/foundation-test-scaffolding/src/main/java/io/vertx/core/impl/SyncContext.java b/foundations/foundation-test-scaffolding/src/main/java/io/vertx/core/impl/SyncContext.java deleted file mode 100644 index eb25a08f56f..00000000000 --- a/foundations/foundation-test-scaffolding/src/main/java/io/vertx/core/impl/SyncContext.java +++ /dev/null @@ -1,87 +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 io.vertx.core.impl; - -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.Promise; - -public class SyncContext extends EventLoopContext { - protected VertxInternal owner; - - public SyncContext() { - this(null); - } - - public SyncContext(VertxInternal vertx) { - super(vertx, null, null, null, null, null, null); - } - - @Override - public VertxInternal owner() { - return owner; - } - - public void setOwner(VertxInternal owner) { - this.owner = owner; - } - - @Override - protected void runOnContext(ContextInternal ctx, Handler action) { - action.handle(null); - } - - public static void syncExecuteBlocking(Handler> blockingCodeHandler, - Handler> asyncResultHandler) { - Promise res = Promise.promise(); - - try { - blockingCodeHandler.handle(res); - } catch (Throwable e) { - res.fail(e); - return; - } - - res.future().onComplete(asyncResultHandler); - } - - private static Future syncExecuteBlocking(Handler> blockingCodeHandler) { - Promise res = Promise.promise(); - - try { - blockingCodeHandler.handle(res); - } catch (Throwable e) { - res.fail(e); - return res.future(); - } - - res.complete(); - return res.future(); - } - - @Override - public Future executeBlockingInternal(Handler> action) { - return syncExecuteBlocking(action); - } - - @Override - public void executeBlocking(Handler> blockingCodeHandler, boolean ordered, - Handler> asyncResultHandler) { - syncExecuteBlocking(blockingCodeHandler, asyncResultHandler); - } -} diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/config/ArchaiusUtils.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/config/ArchaiusUtils.java deleted file mode 100644 index be347046daf..00000000000 --- a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/config/ArchaiusUtils.java +++ /dev/null @@ -1,83 +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.servicecomb.foundation.test.scaffolding.config; - -import java.lang.reflect.Field; -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.util.ReflectionUtils; - -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicProperty; -import com.netflix.config.DynamicPropertyFactory; - -public final class ArchaiusUtils { - private static final Field FIELD_INSTANCE = ReflectionUtils.findField(ConfigurationManager.class, "instance"); - - private static final Field FIELD_CUSTOM_CONFIGURATION_INSTALLED = - ReflectionUtils.findField(ConfigurationManager.class, "customConfigurationInstalled"); - - private static final Field FIELD_CONFIG = ReflectionUtils.findField(DynamicPropertyFactory.class, "config"); - - private static final Field FIELD_INITIALIZED_WITH_DEFAULT_CONFIG = - ReflectionUtils.findField(DynamicPropertyFactory.class, "initializedWithDefaultConfig"); - - private static final Field FIELD_DYNAMIC_PROPERTY_SUPPORTIMPL = - ReflectionUtils.findField(DynamicProperty.class, "dynamicPropertySupportImpl"); - - private static final Field FIELD_DYNAMIC_PROPERTY_ALL_PROPS = ReflectionUtils - .findField(DynamicProperty.class, "ALL_PROPS"); - - static { - FIELD_INSTANCE.setAccessible(true); - FIELD_CUSTOM_CONFIGURATION_INSTALLED.setAccessible(true); - FIELD_CONFIG.setAccessible(true); - FIELD_INITIALIZED_WITH_DEFAULT_CONFIG.setAccessible(true); - FIELD_DYNAMIC_PROPERTY_SUPPORTIMPL.setAccessible(true); - FIELD_DYNAMIC_PROPERTY_ALL_PROPS.setAccessible(true); - } - - private ArchaiusUtils() { - } - - @SuppressWarnings("unchecked") - public static void resetConfig() { - ReflectionUtils.setField(FIELD_INSTANCE, null, null); - ReflectionUtils.setField(FIELD_CUSTOM_CONFIGURATION_INSTALLED, null, false); - ReflectionUtils.setField(FIELD_CONFIG, null, null); - ReflectionUtils.setField(FIELD_INITIALIZED_WITH_DEFAULT_CONFIG, null, false); - ReflectionUtils.setField(FIELD_DYNAMIC_PROPERTY_SUPPORTIMPL, null, null); - ((ConcurrentHashMap) ReflectionUtils.getField(FIELD_DYNAMIC_PROPERTY_ALL_PROPS, null)) - .clear(); - } - - public static void setProperty(String key, Object value) { - // ensure have instance - DynamicPropertyFactory.getInstance(); - - ConcurrentCompositeConfiguration config = (ConcurrentCompositeConfiguration) DynamicPropertyFactory - .getBackingConfigurationSource(); - if (value != null) { - config.getConfiguration(0).setProperty(key, value); - return; - } - - config.getConfiguration(0).clearProperty(key); - } -} diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java index 691e6d611d8..5b435b89829 100644 --- a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java @@ -16,6 +16,12 @@ */ package org.apache.servicecomb.foundation.test.scaffolding.log; +import java.io.Closeable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Appender; @@ -23,27 +29,28 @@ import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.layout.PatternLayout; -import java.io.Closeable; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - public class LogCollector implements Closeable { List events = new ArrayList<>(); - Appender appender = new AbstractAppender("LogCollector", null, PatternLayout.createDefaultLayout()) { - @Override - public void append(LogEvent event) { - events.add(event); - } - }; + Appender appender; + + String appenderName; public LogCollector() { LoggerContext ctx = (LoggerContext) LogManager.getContext(false); Configuration config = ctx.getConfiguration(); + appenderName = "LogCollector" + System.nanoTime(); + appender = new AbstractAppender(appenderName, null, PatternLayout.createDefaultLayout(), + true, + Property.EMPTY_ARRAY) { + @Override + public void append(LogEvent event) { + events.add(event); + } + }; appender.start(); config.getRootLogger().addAppender(appender, Level.ALL, null); ctx.updateLoggers(config); @@ -82,11 +89,11 @@ public List getThrowableMessages() { .collect(Collectors.toList()); } - public void teardown() { + public void tearDown() { LoggerContext ctx = (LoggerContext) LogManager.getContext(false); Configuration config = ctx.getConfiguration(); appender.stop(); - config.getRootLogger().removeAppender("LogCollector"); + config.getRootLogger().removeAppender(appenderName); ctx.updateLoggers(config); } @@ -96,6 +103,6 @@ public void clear() { @Override public void close() { - teardown(); + tearDown(); } } diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/User.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/User.java index 0508a67864a..630d48b0ca1 100644 --- a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/User.java +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/User.java @@ -19,8 +19,14 @@ import java.util.List; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; + public class User { public String name; + // An issue not fixed by open api + // see: https://github.com/swagger-api/swagger-core/issues/3484 + @ArraySchema(schema = @Schema(implementation = User.class)) public List friends; } diff --git a/foundations/foundation-vertx/pom.xml b/foundations/foundation-vertx/pom.xml index d670812a782..24556f590f7 100644 --- a/foundations/foundation-vertx/pom.xml +++ b/foundations/foundation-vertx/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb foundations - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT foundation-vertx Java Chassis::Foundations::Vertx @@ -49,7 +49,7 @@ vertx-codegen provided - + org.apache.logging.log4j log4j-slf4j-impl diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AddressResolverConfig.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AddressResolverConfig.java index 2f0f14a231a..2d4ee389642 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AddressResolverConfig.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AddressResolverConfig.java @@ -20,12 +20,10 @@ import java.util.Arrays; import java.util.List; -import org.apache.commons.configuration.Configuration; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.netflix.config.DynamicPropertyFactory; - import io.vertx.core.dns.AddressResolverOptions; public class AddressResolverConfig { @@ -37,84 +35,69 @@ public class AddressResolverConfig { * @param tag config tag, such as sc.consumer or cc.consumer * @return AddressResolverOptions */ - public static AddressResolverOptions getAddressResover(String tag) { - return getAddressResover(tag, null); - } - - /** - * get the target endpoints with custom address resolve config - * @param tag config tag, such as sc.consumer or cc.consumer - * @param configSource get config from special config source - * @return AddressResolverOptions - */ - public static AddressResolverOptions getAddressResover(String tag, Configuration configSource) { + public static AddressResolverOptions getAddressResolverOptions(String tag) { AddressResolverOptions addressResolverOptions = new AddressResolverOptions(); addressResolverOptions - .setServers(getStringListProperty(configSource, + .setServers(getStringListProperty( AddressResolverOptions.DEFAULT_SERVERS, "addressResolver." + tag + ".servers", "addressResolver.servers")); addressResolverOptions - .setOptResourceEnabled(getBooleanProperty(configSource, + .setOptResourceEnabled(getBooleanProperty( AddressResolverOptions.DEFAULT_OPT_RESOURCE_ENABLED, "addressResolver." + tag + ".optResourceEnabled", "addressResolver.optResourceEnabled")); addressResolverOptions - .setCacheMinTimeToLive(getPositiveIntProperty(configSource, + .setCacheMinTimeToLive(getPositiveIntProperty( AddressResolverOptions.DEFAULT_CACHE_MIN_TIME_TO_LIVE, "addressResolver." + tag + ".cacheMinTimeToLive", "addressResolver.cacheMinTimeToLive")); addressResolverOptions - .setCacheMaxTimeToLive(getPositiveIntProperty(configSource, + .setCacheMaxTimeToLive(getPositiveIntProperty( AddressResolverOptions.DEFAULT_CACHE_MAX_TIME_TO_LIVE, "addressResolver." + tag + ".cacheMaxTimeToLive", "addressResolver.cacheMaxTimeToLive")); addressResolverOptions - .setCacheNegativeTimeToLive(getPositiveIntProperty(configSource, + .setCacheNegativeTimeToLive(getPositiveIntProperty( AddressResolverOptions.DEFAULT_CACHE_NEGATIVE_TIME_TO_LIVE, "addressResolver." + tag + ".cacheNegativeTimeToLive", "addressResolver.cacheNegativeTimeToLive")); addressResolverOptions - .setQueryTimeout(getPositiveIntProperty(configSource, + .setQueryTimeout(getPositiveIntProperty( AddressResolverOptions.DEFAULT_QUERY_TIMEOUT, "addressResolver." + tag + ".queryTimeout", "addressResolver.queryTimeout")); addressResolverOptions - .setMaxQueries(getPositiveIntProperty(configSource, + .setMaxQueries(getPositiveIntProperty( AddressResolverOptions.DEFAULT_MAX_QUERIES, "addressResolver." + tag + ".maxQueries", "addressResolver.maxQueries")); addressResolverOptions - .setRdFlag(getBooleanProperty(configSource, + .setRdFlag(getBooleanProperty( AddressResolverOptions.DEFAULT_RD_FLAG, "addressResolver." + tag + ".rdFlag", "addressResolver.rdFlag")); addressResolverOptions - .setSearchDomains(getStringListProperty(configSource, - AddressResolverOptions.DEFAULT_SEACH_DOMAINS, + .setSearchDomains(getStringListProperty( + AddressResolverOptions.DEFAULT_SEARCH_DOMAINS, "addressResolver." + tag + ".searchDomains", "addressResolver.searchDomains")); addressResolverOptions - .setNdots(getPositiveIntProperty(configSource, + .setNdots(getPositiveIntProperty( AddressResolverOptions.DEFAULT_NDOTS, "addressResolver." + tag + ".ndots", "addressResolver.ndots")); addressResolverOptions - .setRotateServers(getBooleanProperty(configSource, + .setRotateServers(getBooleanProperty( AddressResolverOptions.DEFAULT_ROTATE_SERVERS, "addressResolver." + tag + ".rotateServers", "addressResolver.rotateServers")); return addressResolverOptions; } - private static List getStringListProperty(Configuration configSource, - List defaultValue, String... keys) { - configSource = guardConfigSource(configSource); - if (configSource == null) { - return defaultValue; - } + private static List getStringListProperty(List defaultValue, String... keys) { for (String key : keys) { - String[] vals = configSource.getStringArray(key); + String[] vals = LegacyPropertyFactory.getProperty(key, String[].class); if (vals != null && vals.length > 0) { return Arrays.asList(vals); } @@ -122,15 +105,11 @@ private static List getStringListProperty(Configuration configSource, return defaultValue; } - private static int getPositiveIntProperty(Configuration configSource, int defaultValue, String... keys) { - configSource = guardConfigSource(configSource); - if (configSource == null) { - return defaultValue; - } + private static int getPositiveIntProperty(int defaultValue, String... keys) { for (String key : keys) { - Integer val = configSource.getInteger(key, null); + Integer val = LegacyPropertyFactory.getProperty(key, Integer.class); if (val != null && val <= 0) { - LOGGER.warn("Address resover key:{}'s value:{} is not positive, please check!", key, val); + LOGGER.warn("Address resolver key:{}'s value:{} is not positive, please check!", key, val); continue; } if (val != null) { @@ -140,25 +119,14 @@ private static int getPositiveIntProperty(Configuration configSource, int defaul return defaultValue; } - private static boolean getBooleanProperty(Configuration configSource, boolean defaultValue, + private static boolean getBooleanProperty(boolean defaultValue, String... keys) { - configSource = guardConfigSource(configSource); - if (configSource == null) { - return defaultValue; - } for (String key : keys) { - Boolean val = configSource.getBoolean(key, null); + Boolean val = LegacyPropertyFactory.getProperty(key, Boolean.class); if (val != null) { return val; } } return defaultValue; } - - private static Configuration guardConfigSource(Configuration configSource) { - if (configSource == null && DynamicPropertyFactory.getBackingConfigurationSource() != null) { - configSource = (Configuration) DynamicPropertyFactory.getBackingConfigurationSource(); - } - return configSource; - } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SharedVertxFactory.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SharedVertxFactory.java index 5e35641a241..9f2d3af797b 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SharedVertxFactory.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SharedVertxFactory.java @@ -18,8 +18,7 @@ import org.apache.servicecomb.foundation.vertx.metrics.DefaultVertxMetricsFactory; import org.apache.servicecomb.foundation.vertx.metrics.MetricsOptionsEx; - -import com.netflix.config.DynamicPropertyFactory; +import org.springframework.core.env.Environment; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; @@ -33,13 +32,13 @@ static class SharedVertxInfo implements Shareable { public MetricsOptionsEx metricsOptionsEx = (MetricsOptionsEx) metricsFactory.newOptions(); - public SharedVertxInfo() { + public SharedVertxInfo(Environment environment) { vertxOptions.setMetricsOptions(metricsOptionsEx); - vertxOptions.setEventLoopPoolSize(readEventLoopPoolSize("servicecomb.transport.eventloop.size")); + vertxOptions.setEventLoopPoolSize(readEventLoopPoolSize(environment, "servicecomb.transport.eventloop.size")); } - private static int readEventLoopPoolSize(String key) { - int count = DynamicPropertyFactory.getInstance().getIntProperty(key, -1).get(); + private static int readEventLoopPoolSize(Environment environment, String key) { + int count = environment.getProperty(key, int.class, -1); if (count > 0) { return count; } @@ -51,20 +50,21 @@ private static int readEventLoopPoolSize(String key) { private static final String INFO = "transport-vertx-info"; - public static DefaultVertxMetricsFactory getMetricsFactory() { - SharedVertxInfo info = (SharedVertxInfo) getSharedVertx().sharedData().getLocalMap(LOCAL_MAP_NAME) + public static DefaultVertxMetricsFactory getMetricsFactory(Environment environment) { + SharedVertxInfo info = (SharedVertxInfo) getSharedVertx(environment).sharedData().getLocalMap(LOCAL_MAP_NAME) .get(INFO); return info.metricsFactory; } - public static Vertx getSharedVertx() { - return VertxUtils.getVertxMap().computeIfAbsent("transport", SharedVertxFactory::createSharedVertx); + public static Vertx getSharedVertx(Environment environment) { + return VertxUtils.getVertxMap().computeIfAbsent("transport", + key -> createSharedVertx(environment, key)); } - private static Vertx createSharedVertx(String name) { - SharedVertxInfo info = new SharedVertxInfo(); + private static Vertx createSharedVertx(Environment environment, String name) { + SharedVertxInfo info = new SharedVertxInfo(environment); - Vertx vertx = VertxUtils.init(name, info.vertxOptions); + Vertx vertx = VertxUtils.init(name, info.vertxOptions, info.metricsFactory); info.metricsFactory.setVertx(vertx, info.vertxOptions); vertx.sharedData().getLocalMap(LOCAL_MAP_NAME).put(INFO, info); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SimpleBodyHandler.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SimpleBodyHandler.java index afced60db00..7f089ead415 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SimpleBodyHandler.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SimpleBodyHandler.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.foundation.vertx; -import javax.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status; import org.apache.http.HttpHeaders; diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxTLSBuilder.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxTLSBuilder.java index fae651c4512..ba2203338e6 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxTLSBuilder.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxTLSBuilder.java @@ -23,6 +23,7 @@ import java.util.HashSet; import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLManager; import org.apache.servicecomb.foundation.ssl.SSLOption; @@ -32,6 +33,7 @@ import io.vertx.core.http.ClientAuth; import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.WebSocketClientOptions; import io.vertx.core.net.ClientOptionsBase; import io.vertx.core.net.JksOptions; import io.vertx.core.net.NetServerOptions; @@ -75,10 +77,10 @@ private static void setClientAuth(SSLOption sslOption, NetServerOptions netServe } public static void buildHttpClientOptions(String sslKey, HttpClientOptions httpClientOptions) { - SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory(sslKey, null); + SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory(sslKey, LegacyPropertyFactory.getEnvironment()); SSLOption sslOption; if (factory == null) { - sslOption = SSLOption.buildFromYaml(sslKey); + sslOption = SSLOption.build(sslKey, LegacyPropertyFactory.getEnvironment()); } else { sslOption = factory.createSSLOption(); } @@ -86,6 +88,18 @@ public static void buildHttpClientOptions(String sslKey, HttpClientOptions httpC buildHttpClientOptions(sslOption, sslCustom, httpClientOptions); } + public static void buildWebSocketClientOptions(String sslKey, WebSocketClientOptions webSocketClientOptions) { + SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory(sslKey, LegacyPropertyFactory.getEnvironment()); + SSLOption sslOption; + if (factory == null) { + sslOption = SSLOption.build(sslKey, LegacyPropertyFactory.getEnvironment()); + } else { + sslOption = factory.createSSLOption(); + } + SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); + buildWebSocketClientOptions(sslOption, sslCustom, webSocketClientOptions); + } + public static HttpClientOptions buildHttpClientOptions(SSLOption sslOption, SSLCustom sslCustom, HttpClientOptions httpClientOptions) { buildClientOptionsBase(sslOption, sslCustom, httpClientOptions); @@ -93,6 +107,13 @@ public static HttpClientOptions buildHttpClientOptions(SSLOption sslOption, SSLC return httpClientOptions; } + public static WebSocketClientOptions buildWebSocketClientOptions(SSLOption sslOption, SSLCustom sslCustom, + WebSocketClientOptions webSocketClientOptions) { + buildClientOptionsBase(sslOption, sslCustom, webSocketClientOptions); + webSocketClientOptions.setVerifyHost(sslOption.isCheckCNHost()); + return webSocketClientOptions; + } + public static ClientOptionsBase buildClientOptionsBase(SSLOption sslOption, SSLCustom sslCustom, ClientOptionsBase clientOptionsBase) { buildTCPSSLOptions(sslOption, sslCustom, clientOptionsBase); @@ -110,7 +131,7 @@ private static TCPSSLOptions buildTCPSSLOptions(SSLOption sslOption, SSLCustom s tcpClientOptions.setSsl(true); if (sslOption.getEngine().equalsIgnoreCase("openssl")) { - tcpClientOptions.setOpenSslEngineOptions(new OpenSSLEngineOptions()); + tcpClientOptions.setSslEngineOptions(new OpenSSLEngineOptions()); } String fullKeyStore = sslCustom.getFullPath(sslOption.getKeyStore()); if (isFileExists(fullKeyStore)) { @@ -118,12 +139,12 @@ private static TCPSSLOptions buildTCPSSLOptions(SSLOption sslOption, SSLCustom s PfxOptions keyPfxOptions = new PfxOptions(); keyPfxOptions.setPath(fullKeyStore); keyPfxOptions.setPassword(new String(sslCustom.decode(sslOption.getKeyStoreValue().toCharArray()))); - tcpClientOptions.setPfxKeyCertOptions(keyPfxOptions); + tcpClientOptions.setKeyCertOptions(keyPfxOptions); } else if (STORE_JKS.equalsIgnoreCase(sslOption.getKeyStoreType())) { JksOptions keyJksOptions = new JksOptions(); keyJksOptions.setPath(fullKeyStore); keyJksOptions.setPassword(new String(sslCustom.decode(sslOption.getKeyStoreValue().toCharArray()))); - tcpClientOptions.setKeyStoreOptions(keyJksOptions); + tcpClientOptions.setKeyCertOptions(keyJksOptions); } else { throw new IllegalArgumentException("invalid key store type."); } @@ -137,13 +158,13 @@ private static TCPSSLOptions buildTCPSSLOptions(SSLOption sslOption, SSLCustom s trustPfxOptions.setPath(fullTrustStore); trustPfxOptions .setPassword(new String(sslCustom.decode(sslOption.getTrustStoreValue().toCharArray()))); - tcpClientOptions.setPfxTrustOptions(trustPfxOptions); + tcpClientOptions.setTrustOptions(trustPfxOptions); } else if (STORE_JKS.equalsIgnoreCase(sslOption.getTrustStoreType())) { JksOptions trustJksOptions = new JksOptions(); trustJksOptions.setPath(fullTrustStore); trustJksOptions .setPassword(new String(sslCustom.decode(sslOption.getTrustStoreValue().toCharArray()))); - tcpClientOptions.setTrustStoreOptions(trustJksOptions); + tcpClientOptions.setTrustOptions(trustJksOptions); } else { throw new IllegalArgumentException("invalid trust store type."); } @@ -173,10 +194,14 @@ private static boolean isFileExists(String name) { return true; } - ClassLoader classLoader = - Thread.currentThread().getContextClassLoader() == null ? VertxTLSBuilder.class.getClassLoader() - : Thread.currentThread().getContextClassLoader(); - URL resource = classLoader.getResource(name); - return resource != null; + try { + ClassLoader classLoader = + Thread.currentThread().getContextClassLoader() == null ? VertxTLSBuilder.class.getClassLoader() + : Thread.currentThread().getContextClassLoader(); + URL resource = classLoader.getResource(name); + return resource != null; + } catch (Exception e) { + return false; + } } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxUtils.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxUtils.java index a3f7efa420b..5eceac0b24d 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxUtils.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxUtils.java @@ -20,14 +20,14 @@ import java.io.IOException; import java.io.InputStream; import java.lang.management.ManagementFactory; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import io.vertx.core.file.impl.FileResolverImpl; import org.apache.commons.io.IOUtils; -import org.apache.servicecomb.foundation.common.Holder; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager; import org.apache.servicecomb.foundation.vertx.client.ClientVerticle; @@ -35,18 +35,20 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.netflix.config.DynamicPropertyFactory; - import io.netty.buffer.ByteBuf; import io.vertx.core.AbstractVerticle; import io.vertx.core.DeploymentOptions; +import io.vertx.core.Future; import io.vertx.core.Verticle; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.buffer.Buffer; -import io.vertx.core.impl.VertxBuilder; +import io.vertx.core.impl.SysProps; import io.vertx.core.impl.VertxThread; +import io.vertx.core.internal.VertxBootstrap; +import io.vertx.core.spi.VertxMetricsFactory; import io.vertx.core.spi.VertxThreadFactory; +import io.vertx.core.transport.Transport; /** * VertxUtils @@ -85,17 +87,19 @@ public static DeploymentOptions createClientDeployOptions( } // deploy Verticle and wait for its success. do not call this method in event-loop thread - public static boolean blockDeploy(Vertx vertx, + public static Map blockDeploy(Vertx vertx, Class cls, DeploymentOptions options) throws InterruptedException { - Holder result = new Holder<>(); + Map result = new HashMap<>(); CountDownLatch latch = new CountDownLatch(1); - vertx.deployVerticle(cls.getName(), options, ar -> { - result.value = ar.succeeded(); + Future future = vertx.deployVerticle(cls.getName(), options); + future.onComplete((success, failure) -> { + result.put("code", failure == null); - if (ar.failed()) { - LOGGER.error("deploy vertx failed, cause ", ar.cause()); + if (failure != null) { + result.put("message", failure.getMessage()); + LOGGER.error("deploy vertx failed, cause ", failure); } latch.countDown(); @@ -103,14 +107,15 @@ public static boolean blockDeploy(Vertx vertx, latch.await(); - return result.value; + return result; } - public static Vertx getOrCreateVertxByName(String name, VertxOptions vertxOptions) { - return vertxMap.computeIfAbsent(name, vertxName -> init(vertxName, vertxOptions)); + public static Vertx getOrCreateVertxByName(String name, VertxOptions vertxOptions, + VertxMetricsFactory metricsFactory) { + return vertxMap.computeIfAbsent(name, vertxName -> init(name, vertxOptions, metricsFactory)); } - public static Vertx init(String name, VertxOptions vertxOptions) { + public static Vertx init(String name, VertxOptions vertxOptions, VertxMetricsFactory metricsFactory) { if (vertxOptions == null) { vertxOptions = new VertxOptions(); } @@ -123,22 +128,39 @@ public static Vertx init(String name, VertxOptions vertxOptions) { configureVertxFileCaching(vertxOptions); - return new VertxBuilder(vertxOptions).threadFactory(new VertxThreadFactory() { - @Override - public VertxThread newVertxThread(Runnable target, String threadName, boolean worker, long maxExecTime, - TimeUnit maxExecTimeUnit) { - return VertxThreadFactory.super - .newVertxThread(target, name + "-" + threadName, worker, maxExecTime, maxExecTimeUnit); - } - }).init().vertx(); + VertxBootstrap bootstrap = bootstrap(vertxOptions, metricsFactory) + .threadFactory(new VertxThreadFactory() { + @Override + public VertxThread newVertxThread(Runnable target, String threadName, boolean worker, long maxExecTime, + TimeUnit maxExecTimeUnit) { + return VertxThreadFactory.super + .newVertxThread(target, name + "-" + threadName, worker, maxExecTime, maxExecTimeUnit); + } + }); + + return bootstrap.init().vertx(); + } + + private static VertxBootstrap bootstrap(VertxOptions options, VertxMetricsFactory metricsFactory) { + VertxBootstrap bootstrap = VertxBootstrap.create(); + bootstrap.options(options); + bootstrap.metricsFactory(metricsFactory); + Transport tr; + if (options.getPreferNativeTransport()) { + tr = Transport.nativeTransport(); + } else { + tr = Transport.NIO; + } + bootstrap.transport(tr.implementation()); + return bootstrap; } /** * 配置vertx的文件缓存功能,默认关闭 */ private static void configureVertxFileCaching(VertxOptions vertxOptions) { - boolean disableFileCPResolving = DynamicPropertyFactory.getInstance() - .getBooleanProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, true).get(); + boolean disableFileCPResolving = LegacyPropertyFactory + .getBooleanProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, true); vertxOptions.getFileSystemOptions().setClassPathResolvingEnabled(!disableFileCPResolving); } @@ -153,8 +175,9 @@ public static byte[] getBytesFast(InputStream inputStream) throws IOException { } public static byte[] getBytesFast(Buffer buffer) { - ByteBuf byteBuf = buffer.getByteBuf(); - return getBytesFast(byteBuf); + byte[] arr = new byte[buffer.length()]; + buffer.getBytes(arr, 0); + return arr; } public static byte[] getBytesFast(ByteBuf byteBuf) { @@ -177,22 +200,24 @@ public static CompletableFuture closeVertxByName(String name) { return future; } - vertx.close(ar -> { - if (ar.succeeded()) { + Future closeFuture = vertx.close(); + closeFuture.onComplete((succ, fail) -> { + if (fail == null) { LOGGER.info("Success to close vertx {}.", name); future.complete(null); return; } - future.completeExceptionally(ar.cause()); + future.completeExceptionally(fail); }); + return future; } public static void blockCloseVertxByName(String name) { CompletableFuture future = closeVertxByName(name); try { - future.get(); + future.get(30, TimeUnit.SECONDS); } catch (Throwable e) { LOGGER.error("Failed to wait close vertx {}.", name, e); } @@ -200,8 +225,9 @@ public static void blockCloseVertxByName(String name) { public static void blockCloseVertx(Vertx vertx) { CountDownLatch latch = new CountDownLatch(1); - vertx.close(ar -> { - if (ar.succeeded()) { + Future closeFuture = vertx.close(); + closeFuture.onComplete((succ, fail) -> { + if (fail == null) { LOGGER.info("Success to close vertx {}.", vertx); } else { LOGGER.info("Failed to close vertx {}.", vertx); @@ -211,8 +237,8 @@ public static void blockCloseVertx(Vertx vertx) { }); try { - latch.await(); - } catch (InterruptedException e) { + latch.await(30, TimeUnit.SECONDS); + } catch (Throwable e) { LOGGER.info("Failed to wait close vertx {}.", vertx); } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientOptionsSPI.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientOptionsSPI.java index 164e4f4dc4c..83bf28db9ca 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientOptionsSPI.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientOptionsSPI.java @@ -20,10 +20,10 @@ import org.apache.servicecomb.foundation.common.encrypt.Encryptions; import org.apache.servicecomb.foundation.vertx.VertxTLSBuilder; -import com.netflix.config.ConcurrentCompositeConfiguration; - import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpVersion; +import io.vertx.core.http.WebSocketClientOptions; +import io.vertx.core.net.ClientOptionsBase; import io.vertx.core.net.ProxyOptions; /** @@ -40,12 +40,9 @@ public interface HttpClientOptionsSPI { boolean enabled(); /* config tag is used for group configurations, like ssl, address resolver, etc. set config tag to distinguish - * other clients configuration or read the common configuration. */ + * other clients configuration or read the common configuration. */ String getConfigTag(); - /* for config modules, the configuration is not ready, need set up config reader */ - ConcurrentCompositeConfiguration getConfigReader(); - /***************** vert.x common settings ***************************/ int getEventLoopPoolSize(); @@ -79,12 +76,15 @@ public interface HttpClientOptionsSPI { int getKeepAliveTimeout(); + boolean enableLogActivity(); + /***************** http 2 settings ****************************/ int getHttp2MultiplexingLimit(); int getHttp2MaxPoolSize(); boolean isUseAlpn(); + /***************** proxy settings ***************************/ boolean isProxyEnable(); @@ -99,18 +99,10 @@ public interface HttpClientOptionsSPI { /***************** ssl settings ***************************/ boolean isSsl(); - static HttpClientOptions createHttpClientOptions(HttpClientOptionsSPI spi) { - HttpClientOptions httpClientOptions = new HttpClientOptions(); - - httpClientOptions.setProtocolVersion(spi.getHttpVersion()); + static void buildClientOptionsBase(HttpClientOptionsSPI spi, ClientOptionsBase httpClientOptions) { httpClientOptions.setConnectTimeout(spi.getConnectTimeoutInMillis()); httpClientOptions.setIdleTimeout(spi.getIdleTimeoutInSeconds()); - httpClientOptions.setTryUseCompression(spi.isTryUseCompression()); - httpClientOptions.setMaxWaitQueueSize(spi.getMaxWaitQueueSize()); - httpClientOptions.setMaxPoolSize(spi.getMaxPoolSize()); - httpClientOptions.setKeepAlive(spi.isKeepAlive()); - httpClientOptions.setMaxHeaderSize(spi.getMaxHeaderSize()); - httpClientOptions.setKeepAliveTimeout(spi.getKeepAliveTimeout()); + httpClientOptions.setLogActivity(spi.enableLogActivity()); if (spi.isProxyEnable()) { ProxyOptions proxy = new ProxyOptions(); @@ -123,10 +115,25 @@ static HttpClientOptions createHttpClientOptions(HttpClientOptionsSPI spi) { } if (spi.getHttpVersion() == HttpVersion.HTTP_2) { - httpClientOptions.setHttp2ClearTextUpgrade(false); httpClientOptions.setUseAlpn(spi.isUseAlpn()); + } + } + + static HttpClientOptions createHttpClientOptions(HttpClientOptionsSPI spi) { + HttpClientOptions httpClientOptions = new HttpClientOptions(); + buildClientOptionsBase(spi, httpClientOptions); + + httpClientOptions.setProtocolVersion(spi.getHttpVersion()); + httpClientOptions.setDecompressionSupported(spi.isTryUseCompression()); + httpClientOptions.setKeepAlive(spi.isKeepAlive()); + httpClientOptions.setMaxHeaderSize(spi.getMaxHeaderSize()); + + if (spi.getHttpVersion() == HttpVersion.HTTP_2) { + httpClientOptions.setHttp2ClearTextUpgrade(false); httpClientOptions.setHttp2MultiplexingLimit(spi.getHttp2MultiplexingLimit()); - httpClientOptions.setHttp2MaxPoolSize(spi.getHttp2MaxPoolSize()); + httpClientOptions.setHttp2KeepAliveTimeout(spi.getKeepAliveTimeout()); + } else { + httpClientOptions.setKeepAliveTimeout(spi.getKeepAliveTimeout()); } if (spi.isSsl()) { @@ -135,4 +142,15 @@ static HttpClientOptions createHttpClientOptions(HttpClientOptionsSPI spi) { return httpClientOptions; } + + static WebSocketClientOptions createWebSocketClientOptions(HttpClientOptionsSPI spi, boolean sslEnabled) { + WebSocketClientOptions webSocketClientOptions = new WebSocketClientOptions(); + buildClientOptionsBase(spi, webSocketClientOptions); + + if (sslEnabled) { + VertxTLSBuilder.buildWebSocketClientOptions(spi.getConfigTag(), webSocketClientOptions); + } + + return webSocketClientOptions; + } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientPoolFactory.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientPoolFactory.java index c8752949ddc..69f15e6e397 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientPoolFactory.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientPoolFactory.java @@ -5,9 +5,7 @@ * 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. @@ -24,6 +22,7 @@ import io.vertx.core.Context; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.PoolOptions; // execute in vertx context public class HttpClientPoolFactory implements ClientPoolFactory { @@ -31,25 +30,30 @@ public class HttpClientPoolFactory implements ClientPoolFactory { - LOGGER.debug("http connection connected, local:{}, remote:{}.", - connection.localAddress(), connection.remoteAddress()); - connection.closeHandler(v -> - LOGGER.debug("http connection closed, local:{}, remote:{}.", - connection.localAddress(), connection.remoteAddress()) - ); - connection.exceptionHandler(e -> - LOGGER.info("http connection exception, local:{}, remote:{}.", - connection.localAddress(), connection.remoteAddress(), e) - ); - }); + HttpClient httpClient = context.owner().httpClientBuilder() + .with(httpClientOptions) + .with(poolOptions) + .withConnectHandler(connection -> { + LOGGER.debug("http connection connected, local:{}, remote:{}.", + connection.localAddress(), connection.remoteAddress()); + connection.closeHandler(v -> + LOGGER.debug("http connection closed, local:{}, remote:{}.", + connection.localAddress(), connection.remoteAddress()) + ); + connection.exceptionHandler(e -> + LOGGER.error("http connection exception, local:{}, remote:{}.", + connection.localAddress(), connection.remoteAddress(), e) + ); + }).build(); return new HttpClientWithContext(httpClient, context); } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClients.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClients.java index 06085446caa..b1ef46436ec 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClients.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClients.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.foundation.vertx.AddressResolverConfig; import org.apache.servicecomb.foundation.vertx.SharedVertxFactory; @@ -32,9 +33,12 @@ import io.vertx.core.Context; import io.vertx.core.DeploymentOptions; +import io.vertx.core.ThreadingModel; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.dns.AddressResolverOptions; +import io.vertx.core.http.PoolOptions; +import io.vertx.core.http.WebSocketClient; /** * load and manages a set of HttpClient at boot up. @@ -67,12 +71,16 @@ public static void destroy() { private static ClientPoolManager createClientPoolManager(HttpClientOptionsSPI option) { Vertx vertx = getOrCreateVertx(option); + PoolOptions poolOptions = new PoolOptions(); + poolOptions.setMaxWaitQueueSize(option.getMaxWaitQueueSize()); + poolOptions.setHttp1MaxSize(option.getMaxPoolSize()); + poolOptions.setHttp2MaxSize(option.getHttp2MaxPoolSize()); ClientPoolManager clientPoolManager = new ClientPoolManager<>(vertx, - new HttpClientPoolFactory(HttpClientOptionsSPI.createHttpClientOptions(option))); + new HttpClientPoolFactory(HttpClientOptionsSPI.createHttpClientOptions(option), poolOptions)); DeploymentOptions deployOptions = VertxUtils.createClientDeployOptions(clientPoolManager, option.getInstanceCount()) - .setWorker(option.isWorker()) + .setThreadingModel(option.isWorker() ? ThreadingModel.WORKER : ThreadingModel.EVENT_LOOP) .setWorkerPoolName(option.getWorkerPoolName()) .setWorkerPoolSize(option.getWorkerPoolSize()); try { @@ -83,19 +91,24 @@ private static ClientPoolManager createClientPoolManager( } } + public static WebSocketClient createWebSocketClient(HttpClientOptionsSPI option, boolean sslEnabled) { + Vertx vertx = getOrCreateVertx(option); + return vertx.createWebSocketClient(HttpClientOptionsSPI.createWebSocketClientOptions(option, sslEnabled)); + } + private static Vertx getOrCreateVertx(HttpClientOptionsSPI option) { if (option.useSharedVertx()) { - return SharedVertxFactory.getSharedVertx(); + return SharedVertxFactory.getSharedVertx(LegacyPropertyFactory.getEnvironment()); } AddressResolverOptions resolverOptions = AddressResolverConfig - .getAddressResover(option.getConfigTag(), option.getConfigReader()); + .getAddressResolverOptions(option.getConfigTag()); VertxOptions vertxOptions = new VertxOptions() .setAddressResolverOptions(resolverOptions) .setEventLoopPoolSize(option.getEventLoopPoolSize()); // Maybe we can deploy only one vert.x for the application. However this has did it like this. - return VertxUtils.getOrCreateVertxByName(option.clientName(), vertxOptions); + return VertxUtils.getOrCreateVertxByName(option.clientName(), vertxOptions, null); } /** diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPackage.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPackage.java index 763ba5e3daf..ef6717f8968 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPackage.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPackage.java @@ -27,9 +27,9 @@ public static long getAndIncRequestId() { return reqId.getAndIncrement(); } - private long msRequestTimeout; + private volatile long msRequestTimeout; - private long finishWriteToBuffer; + private volatile long finishWriteToBuffer; protected long msgId = getAndIncRequestId(); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/NetClientWrapper.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/NetClientWrapper.java index a36d80c246c..ce47126ee47 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/NetClientWrapper.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/NetClientWrapper.java @@ -16,8 +16,7 @@ */ package org.apache.servicecomb.foundation.vertx.client.tcp; -import io.vertx.core.AsyncResult; -import io.vertx.core.Handler; +import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.net.NetClient; import io.vertx.core.net.NetSocket; @@ -50,12 +49,11 @@ public TcpClientConfig getClientConfig(boolean ssl) { return normalClientConfig; } - public void connect(boolean ssl, int port, String host, Handler> connectHandler) { + public Future connect(boolean ssl, int port, String host) { if (ssl) { - sslNetClient.connect(port, host, connectHandler); - return; + return sslNetClient.connect(port, host); } - normalNetClient.connect(port, host, connectHandler); + return normalNetClient.connect(port, host); } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java index 5d363e874d9..db071e3c34f 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java @@ -39,6 +39,7 @@ import io.vertx.core.AsyncResult; import io.vertx.core.Context; +import io.vertx.core.Future; import io.vertx.core.buffer.Buffer; import io.vertx.core.net.NetSocket; import io.vertx.core.net.impl.NetSocketImpl; @@ -61,7 +62,7 @@ enum Status { private final InetSocketAddress socketAddress; - private boolean localSupportLogin = false; + private volatile boolean localSupportLogin = false; private final boolean remoteSupportLogin; @@ -144,7 +145,7 @@ private boolean writeToBufferQueue(AbstractTcpClientPackage tcpClientPackage) { if (Status.WORKING.equals(status)) { // encode in sender thread try (TcpOutputStream os = tcpClientPackage.createStream()) { - write(os.getByteBuf()); + write(os.getBuffer()); tcpClientPackage.finishWriteToBuffer(); } return true; @@ -180,17 +181,17 @@ protected void connect() { this.status = Status.CONNECTING; LOGGER.info("connecting to address {}", socketAddress.toString()); - netClientWrapper.connect(endpoint.isSslEnabled(), + Future result = netClientWrapper.connect(endpoint.isSslEnabled(), socketAddress.getPort(), - socketAddress.getHostString(), - ar -> { - if (ar.succeeded()) { - onConnectSuccess(ar.result()); - return; - } - - onConnectFailed(ar.cause()); - }); + socketAddress.getHostString()); + result.onComplete((socket, fail) -> { + if (fail == null) { + onConnectSuccess(socket); + return; + } + + onConnectFailed(fail); + }); } private void onConnectSuccess(NetSocket socket) { diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxWorkerExecutor.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxWorkerExecutor.java index 7f19f91139e..e3334a79f1b 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxWorkerExecutor.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxWorkerExecutor.java @@ -25,8 +25,9 @@ public class VertxWorkerExecutor implements Executor { @Override public void execute(Runnable command) { - Vertx.currentContext().owner().executeBlocking(future -> command.run(), - false, - null); + Vertx.currentContext().owner().executeBlocking(() -> { + command.run(); + return null; + }, false); } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletRequest.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletRequest.java index ac71310e69d..058a9390464 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletRequest.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletRequest.java @@ -28,19 +28,20 @@ import java.util.Locale; import java.util.Map; -import javax.servlet.AsyncContext; -import javax.servlet.DispatcherType; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletInputStream; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import javax.servlet.http.HttpUpgradeHandler; -import javax.servlet.http.Part; +import jakarta.servlet.AsyncContext; +import jakarta.servlet.DispatcherType; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletConnection; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import jakarta.servlet.http.HttpUpgradeHandler; +import jakarta.servlet.http.Part; public abstract class AbstractHttpServletRequest extends BodyBufferSupportImpl implements HttpServletRequestEx { private final Map attributeMap = new HashMap<>(); @@ -65,6 +66,21 @@ public void setCharacterEncoding(String env) throws UnsupportedEncodingException throw new Error("not supported method"); } + @Override + public String getRequestId() { + throw new Error("not supported method"); + } + + @Override + public String getProtocolRequestId() { + throw new Error("not supported method"); + } + + @Override + public ServletConnection getServletConnection() { + throw new Error("not supported method"); + } + @Override public int getContentLength() { throw new Error("not supported method"); @@ -170,12 +186,6 @@ public RequestDispatcher getRequestDispatcher(String path) { throw new Error("not supported method"); } - @Override - @Deprecated - public String getRealPath(String path) { - throw new Error("not supported method"); - } - @Override public int getRemotePort() { throw new Error("not supported method"); @@ -357,12 +367,6 @@ public boolean isRequestedSessionIdFromURL() { throw new Error("not supported method"); } - @Override - @Deprecated - public boolean isRequestedSessionIdFromUrl() { - throw new Error("not supported method"); - } - @Override public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { throw new Error("not supported method"); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java index 34a6088f784..bd7899b471c 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java @@ -25,10 +25,11 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.Cookie; -import javax.servlet.http.Part; -import javax.ws.rs.core.Response.StatusType; +import io.vertx.core.buffer.Buffer; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.Response.StatusType; public abstract class AbstractHttpServletResponse extends BodyBufferSupportImpl implements HttpServletResponseEx { private final Map attributes = new HashMap<>(); @@ -85,6 +86,11 @@ public int getBufferSize() { @Override public void flushBuffer() throws IOException { + // for vert.x do noting + } + + @Override + public void endResponse() throws IOException { throw new Error("not supported method"); } @@ -133,18 +139,6 @@ public String encodeRedirectURL(String url) { throw new Error("not supported method"); } - @Override - @Deprecated - public String encodeUrl(String url) { - throw new Error("not supported method"); - } - - @Override - @Deprecated - public String encodeRedirectUrl(String url) { - throw new Error("not supported method"); - } - @Override public void sendError(int sc, String msg) throws IOException { throw new Error("not supported method"); @@ -195,12 +189,6 @@ public void setStatus(int sc) { throw new Error("not supported method"); } - @Override - @Deprecated - public void setStatus(int sc, String sm) { - throw new Error("not supported method"); - } - @Override public int getStatus() { throw new Error("not supported method"); @@ -240,4 +228,9 @@ public Object getAttribute(String key) { public CompletableFuture sendPart(Part body) { throw new Error("not supported method"); } + + @Override + public CompletableFuture sendBuffer(Buffer buffer) { + throw new Error("not supported method"); + } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/DownloadUtils.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/DownloadUtils.java index f37712e34e1..7c9c0fffbfc 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/DownloadUtils.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/DownloadUtils.java @@ -18,7 +18,7 @@ import java.io.IOException; -import javax.servlet.http.Part; +import jakarta.servlet.http.Part; import org.apache.servicecomb.foundation.common.http.HttpUtils; import org.apache.servicecomb.foundation.common.part.FilePartForSend; @@ -44,14 +44,18 @@ public static void prepareDownloadHeader(HttpServletResponseEx responseEx, Part return; } if (responseEx.getHeader(HttpHeaders.CONTENT_TYPE.toString()) == null) { - responseEx.setHeader(HttpHeaders.CONTENT_TYPE.toString(), part.getContentType()); + if (responseEx.getContentType() != null) { + responseEx.setHeader(HttpHeaders.CONTENT_TYPE.toString(), responseEx.getContentType()); + } else { + responseEx.setHeader(HttpHeaders.CONTENT_TYPE.toString(), part.getContentType()); + } } - if (responseEx.getHeader(javax.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION) == null) { + if (responseEx.getHeader(jakarta.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION) == null) { // to support chinese and space filename in firefox // must use "filename*", (https://tools.ietf.org/html/rtf6266) String encodedFileName = HttpUtils.uriEncodePath(part.getSubmittedFileName()); - responseEx.setHeader(javax.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION, + responseEx.setHeader(jakarta.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + encodedFileName + ";filename*=utf-8''" + encodedFileName); } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/EmptyAsyncContext.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/EmptyAsyncContext.java index dd9f6acf0f2..142435ead3e 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/EmptyAsyncContext.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/EmptyAsyncContext.java @@ -17,12 +17,12 @@ package org.apache.servicecomb.foundation.vertx.http; -import javax.servlet.AsyncContext; -import javax.servlet.AsyncListener; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; +import jakarta.servlet.AsyncContext; +import jakarta.servlet.AsyncListener; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; public class EmptyAsyncContext implements AsyncContext { @Override diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/FileUploadPart.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/FileUploadPart.java index a444495f675..9e72d64d289 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/FileUploadPart.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/FileUploadPart.java @@ -18,9 +18,9 @@ package org.apache.servicecomb.foundation.vertx.http; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.foundation.common.part.AbstractPart; @@ -36,7 +36,7 @@ public FileUploadPart(FileUpload fileUpload) { @Override public InputStream getInputStream() throws IOException { - return new FileInputStream(fileUpload.uploadedFileName()); + return Files.newInputStream(new File(fileUpload.uploadedFileName()).toPath()); } @Override diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletRequestEx.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletRequestEx.java index d2f62f2b0fe..701b117ab6c 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletRequestEx.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletRequestEx.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.foundation.vertx.http; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; public interface HttpServletRequestEx extends HttpServletRequest, BodyBufferSupport { default void setHeader(String name, String value) { diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.java index e1c2158d743..a565ed7791e 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.java @@ -17,13 +17,16 @@ package org.apache.servicecomb.foundation.vertx.http; +import java.io.IOException; +import java.io.PrintWriter; import java.util.concurrent.CompletableFuture; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.Part; -import javax.ws.rs.core.Response.StatusType; - +import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpHeaders; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.Response.StatusType; public interface HttpServletResponseEx extends HttpServletResponse, BodyBufferSupport { StatusType getStatusType(); @@ -34,7 +37,21 @@ public interface HttpServletResponseEx extends HttpServletResponse, BodyBufferSu CompletableFuture sendPart(Part body); + CompletableFuture sendBuffer(Buffer buffer); + default void setChunked(boolean chunked) { setHeader(HttpHeaders.TRANSFER_ENCODING.toString(), HttpHeaders.CHUNKED.toString()); } + + void endResponse() throws IOException; + + @Override + default ServletOutputStream getOutputStream() throws IOException { + throw new IOException("Not allowed"); + } + + @Override + default PrintWriter getWriter() throws IOException { + throw new IOException("Not allowed"); + } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/ReadStreamPart.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/ReadStreamPart.java index fe6fdd9133c..acd2fc1978a 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/ReadStreamPart.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/ReadStreamPart.java @@ -20,18 +20,15 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Function; -import javax.servlet.http.Part; -import javax.ws.rs.core.HttpHeaders; - -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.http.HttpUtils; import org.apache.servicecomb.foundation.common.part.AbstractPart; import org.apache.servicecomb.foundation.vertx.stream.PumpCommon; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.vertx.core.AsyncResult; import io.vertx.core.Context; +import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.file.AsyncFile; @@ -39,6 +36,8 @@ import io.vertx.core.http.HttpClientResponse; import io.vertx.core.streams.ReadStream; import io.vertx.core.streams.WriteStream; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.HttpHeaders; /** * this is not a really part type, all method extend from AbstractPart is undefined except:
@@ -83,15 +82,13 @@ public ReadStream getReadStream() { } /** - * - * @param writeStream * @return future of save action
* * important: WriteStream did not provide endHandler, so we can not know when will really finished write. * so the return future only means finished read from readStream. */ public CompletableFuture saveToWriteStream(WriteStream writeStream) { - return new PumpCommon().pump(context, readStream, writeStream, null); + return new PumpCommon().pump(readStream, writeStream, null); } public CompletableFuture saveAsBytes() { @@ -121,8 +118,6 @@ public CompletableFuture saveAs(Function converter) { } /** - * - * @param fileName * @return future of save to file, future complete means write to file finished */ public CompletableFuture saveToFile(String fileName) { @@ -133,9 +128,6 @@ public CompletableFuture saveToFile(String fileName) { } /** - * - * @param file - * @param openOptions * @return future of save to file, future complete means write to file finished */ public CompletableFuture saveToFile(File file, OpenOptions openOptions) { @@ -143,34 +135,37 @@ public CompletableFuture saveToFile(File file, OpenOptions openOptions) { context.runOnContext((v) -> { Vertx vertx = context.owner(); - vertx.fileSystem().open(file.getAbsolutePath(), openOptions, ar -> onFileOpened(file, ar, future)); + Future openFuture = vertx.fileSystem().open(file.getAbsolutePath(), openOptions); + openFuture.onComplete((s, f) -> onFileOpened(file, s, f, future)); }); return future; } - protected void onFileOpened(File file, AsyncResult ar, CompletableFuture future) { - if (ar.failed()) { - future.completeExceptionally(ar.cause()); + protected void onFileOpened(File file, AsyncFile asyncFile, Throwable failure, CompletableFuture future) { + if (failure != null) { + future.completeExceptionally(failure); return; } - AsyncFile asyncFile = ar.result(); CompletableFuture saveFuture = saveToWriteStream(asyncFile); - saveFuture.whenComplete((v, saveException) -> asyncFile.close(closeAr -> { - if (closeAr.failed()) { - LOGGER.error("Failed to close file {}.", file); - } - - // whatever close success or failed - // will not affect to result - // result just only related to write - if (saveException == null) { - future.complete(file); - return; - } - - future.completeExceptionally(saveException); - })); + saveFuture.whenComplete((v, saveException) -> { + Future result = asyncFile.close(); + result.onComplete((s, f) -> { + if (f != null) { + LOGGER.error("Failed to close file {}.", file); + } + + // whatever close success or failed + // will not affect to result + // result just only related to write + if (saveException == null) { + future.complete(file); + return; + } + + future.completeExceptionally(saveException); + }); + }); } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletRequestEx.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletRequestEx.java index 30145749fc3..82b46b47597 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletRequestEx.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletRequestEx.java @@ -30,22 +30,20 @@ import java.util.Map; import java.util.Map.Entry; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.core.MediaType; - -import com.google.common.annotations.VisibleForTesting; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; +import com.google.common.annotations.VisibleForTesting; + import io.vertx.core.buffer.Buffer; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import jakarta.ws.rs.HttpMethod; +import jakarta.ws.rs.core.MediaType; public class StandardHttpServletRequestEx extends HttpServletRequestWrapper implements HttpServletRequestEx { private final BodyBufferSupport bodyBuffer = new BodyBufferSupportImpl(); @@ -77,9 +75,9 @@ public ServletInputStream getInputStream() throws IOException { if (this.inputStream == null) { if (cacheRequest) { byte[] inputBytes = IOUtils.toByteArray(getRequest().getInputStream()); - ByteBuf byteBuf = Unpooled.wrappedBuffer(inputBytes); + Buffer byteBuf = Buffer.buffer(inputBytes); this.inputStream = new BufferInputStream(byteBuf); - setBodyBuffer(Buffer.buffer(Unpooled.wrappedBuffer(byteBuf))); + setBodyBuffer(byteBuf); } else { this.inputStream = getRequest().getInputStream(); } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java index e2b51e84d08..38db248efdb 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java @@ -23,18 +23,16 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpServletResponseWrapper; -import javax.servlet.http.Part; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.Response.StatusType; - -import org.apache.servicecomb.foundation.common.http.HttpStatus; import org.apache.servicecomb.foundation.vertx.stream.PumpFromPart; import io.vertx.core.Context; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.StatusType; public class StandardHttpServletResponseEx extends HttpServletResponseWrapper implements HttpServletResponseEx { private final BodyBufferSupport bodyBuffer = new BodyBufferSupportImpl(); @@ -67,13 +65,6 @@ public int getBodyBytesLength() { return bodyBuffer.getBodyBytesLength(); } - @SuppressWarnings("deprecation") - @Override - public void setStatus(int sc, String sm) { - super.setStatus(sc, sm); - statusType = new HttpStatus(sc, sm); - } - @Override public void setStatus(int sc) { super.setStatus(sc); @@ -91,11 +82,7 @@ public StatusType getStatusType() { } @Override - public void flushBuffer() throws IOException { - byte[] bytes = getBodyBytes(); - if (bytes != null) { - getOutputStream().write(bytes, 0, getBodyBytesLength()); - } + public void endResponse() throws IOException { super.flushBuffer(); } @@ -130,4 +117,16 @@ public CompletableFuture sendPart(Part part) { Context context = Vertx.currentContext(); return new PumpFromPart(context, part).toOutputStream(outputStream, false); } + + @Override + public CompletableFuture sendBuffer(Buffer buffer) { + CompletableFuture future = new CompletableFuture<>(); + try { + getOutputStream().write(buffer.getBytes(), 0, buffer.length()); + future.complete(null); + } catch (IOException e) { + future.completeExceptionally(e); + } + return future; + } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxClientRequestToHttpServletRequest.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxClientRequestToHttpServletRequest.java index 7e540703f72..8a54b8d835f 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxClientRequestToHttpServletRequest.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxClientRequestToHttpServletRequest.java @@ -20,12 +20,11 @@ import java.util.Collections; import java.util.Enumeration; -import javax.ws.rs.core.HttpHeaders; - import org.apache.servicecomb.foundation.common.http.HttpUtils; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientRequest; +import jakarta.ws.rs.core.HttpHeaders; public class VertxClientRequestToHttpServletRequest extends AbstractHttpServletRequest { private final HttpClientRequest clientRequest; @@ -42,6 +41,11 @@ public String getRequestURI() { return clientRequest.path(); } + @Override + public StringBuffer getRequestURL() { + return new StringBuffer(clientRequest.path()); + } + @Override public String getQueryString() { return clientRequest.query(); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxClientResponseToHttpServletResponse.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxClientResponseToHttpServletResponse.java index b1cbef217fe..da7e532174b 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxClientResponseToHttpServletResponse.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxClientResponseToHttpServletResponse.java @@ -19,8 +19,8 @@ import java.util.Collection; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.Response.StatusType; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.foundation.common.http.HttpStatus; diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java index 435b660bc62..da55f5ff613 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java @@ -27,13 +27,6 @@ import java.util.Set; import java.util.stream.Collectors; -import javax.servlet.AsyncContext; -import javax.servlet.ServletInputStream; -import javax.servlet.http.Cookie; -import javax.servlet.http.Part; -import javax.ws.rs.core.HttpHeaders; - -import io.vertx.ext.web.impl.RoutingContextInternal; import org.apache.servicecomb.foundation.common.http.HttpUtils; import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; import org.slf4j.Logger; @@ -45,6 +38,12 @@ import io.vertx.core.net.SocketAddress; import io.vertx.ext.web.FileUpload; import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.impl.RoutingContextInternal; +import jakarta.servlet.AsyncContext; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.HttpHeaders; // wrap vertx http request to Servlet http request public class VertxServerRequestToHttpServletRequest extends AbstractHttpServletRequest { @@ -243,6 +242,14 @@ public String getRequestURI() { return this.path; } + @Override + public StringBuffer getRequestURL() { + if (this.path == null) { + this.path = vertxRequest.path(); + } + return new StringBuffer(this.path); + } + @Override public String getServletPath() { return this.getPathInfo(); @@ -261,7 +268,7 @@ public ServletInputStream getInputStream() { if (context.body().buffer() == null) { return null; } - inputStream = new BufferInputStream(context.body().buffer().getByteBuf()); + inputStream = new BufferInputStream(context.body().buffer()); return inputStream; } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java index 668ce0022b5..81785e9698c 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java @@ -17,20 +17,21 @@ package org.apache.servicecomb.foundation.vertx.http; +import java.io.IOException; import java.util.Collection; import java.util.Objects; import java.util.concurrent.CompletableFuture; -import javax.servlet.http.Part; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.Response.StatusType; - import org.apache.servicecomb.foundation.common.http.HttpStatus; import org.apache.servicecomb.foundation.vertx.stream.PumpFromPart; import io.vertx.core.Context; import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpServerResponse; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response.StatusType; public class VertxServerResponseToHttpServletResponse extends AbstractHttpServletResponse { private final Context context; @@ -52,12 +53,11 @@ public void setContentType(String type) { } @Override - @Deprecated - public void setStatus(int sc, String sm) { - serverResponse.setStatusCode(sc); - serverResponse.setStatusMessage(sm); + public void setContentLength(int len) { + serverResponse.headers().set(HttpHeaders.CONTENT_LENGTH, String.valueOf(len)); } + @Override public void setStatus(int sc) { serverResponse.setStatusCode(sc); @@ -107,7 +107,7 @@ public Collection getHeaderNames() { } @Override - public void flushBuffer() { + public void endResponse() { if (context == Vertx.currentContext()) { internalFlushBuffer(); return; @@ -117,12 +117,10 @@ public void flushBuffer() { } public void internalFlushBuffer() { - if (bodyBuffer == null) { - serverResponse.end(); + if (serverResponse.closed()) { return; } - - serverResponse.end(bodyBuffer); + serverResponse.end(); } @Override @@ -134,6 +132,23 @@ public CompletableFuture sendPart(Part part) { return new PumpFromPart(context, part).toWriteStream(serverResponse, null); } + @Override + public CompletableFuture sendBuffer(Buffer buffer) { + if (serverResponse.closed()) { + return CompletableFuture.failedFuture(new IOException("Response is closed before sending any data. " + + "Maybe client is timeout or check idle connection timeout for provider is properly configured.")); + } + CompletableFuture future = new CompletableFuture<>(); + serverResponse.write(buffer).onComplete(result -> { + if (result.failed()) { + future.completeExceptionally(result.cause()); + } else { + future.complete(null); + } + }); + return future; + } + @Override public void setChunked(boolean chunked) { serverResponse.setChunked(chunked); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultClientMetrics.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultClientMetrics.java index c3893a2e60d..2bc9288857b 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultClientMetrics.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultClientMetrics.java @@ -17,16 +17,14 @@ package org.apache.servicecomb.foundation.vertx.metrics; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientTaskMetric; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultRequestMetric; - import io.vertx.core.spi.metrics.ClientMetrics; import io.vertx.core.spi.observability.HttpRequest; import io.vertx.core.spi.observability.HttpResponse; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultRequestMetric; public class DefaultClientMetrics implements - ClientMetrics { + ClientMetrics { private final DefaultClientEndpointMetric clientEndpointMetric; public DefaultClientMetrics(DefaultClientEndpointMetric clientEndpointMetric) { @@ -37,18 +35,6 @@ public DefaultClientEndpointMetric getClientEndpointMetric() { return this.clientEndpointMetric; } - @Override - public DefaultClientTaskMetric enqueueRequest() { - DefaultClientTaskMetric taskMetric = new DefaultClientTaskMetric(clientEndpointMetric); - taskMetric.enqueueRequest(); - return taskMetric; - } - - @Override - public void dequeueRequest(DefaultClientTaskMetric endpointMetric) { - endpointMetric.dequeueRequest(); - } - @Override public DefaultRequestMetric requestBegin(String uri, HttpRequest request) { DefaultRequestMetric requestMetric = new DefaultRequestMetric(this.clientEndpointMetric); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpClientMetrics.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpClientMetrics.java index 59649948348..1279a4f8566 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpClientMetrics.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpClientMetrics.java @@ -18,7 +18,6 @@ import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetricManager; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientTaskMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultRequestMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; @@ -26,7 +25,7 @@ import io.vertx.core.spi.metrics.HttpClientMetrics; public class DefaultHttpClientMetrics implements - HttpClientMetrics { + HttpClientMetrics { private static final String PROTOCOL = "http://"; private final DefaultClientEndpointMetricManager clientEndpointMetricManager; diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetrics.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetrics.java index ee345ed4e2b..c0b89a42eea 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetrics.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetrics.java @@ -18,23 +18,22 @@ import java.util.Map; -import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetricManager; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; - import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpServerOptions; -import io.vertx.core.metrics.impl.DummyVertxMetrics; import io.vertx.core.net.NetClientOptions; import io.vertx.core.net.NetServerOptions; import io.vertx.core.net.SocketAddress; import io.vertx.core.spi.metrics.HttpClientMetrics; import io.vertx.core.spi.metrics.HttpServerMetrics; import io.vertx.core.spi.metrics.TCPMetrics; +import io.vertx.core.spi.metrics.VertxMetrics; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetricManager; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; -public class DefaultVertxMetrics extends DummyVertxMetrics { +public class DefaultVertxMetrics implements VertxMetrics { // to support listen multiple addresses, must use a map to manage the metric private final Map serverEndpointMetricMap = new ConcurrentHashMapEx<>(); @@ -61,7 +60,7 @@ public Map getServerEndpointMetricMap() { } @Override - public HttpClientMetrics createHttpClientMetrics(HttpClientOptions options) { + public HttpClientMetrics createHttpClientMetrics(HttpClientOptions options) { return new DefaultHttpClientMetrics(clientEndpointMetricManager); } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetricsFactory.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetricsFactory.java index dca7cecca92..540a644b1e1 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetricsFactory.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetricsFactory.java @@ -55,7 +55,6 @@ public synchronized VertxMetrics metrics(VertxOptions options) { @Override public MetricsOptions newOptions() { MetricsOptionsEx metricsOptions = new MetricsOptionsEx(); - metricsOptions.setFactory(this); metricsOptions.setEnabled(true); return metricsOptions; } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetric.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetric.java index cedd6ec6b32..ce618078957 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetric.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetric.java @@ -29,7 +29,7 @@ public class DefaultClientEndpointMetric extends DefaultEndpointMetric { // control if the metric instance will be expired // all invoker about incRefCount/isExpired, must lock: DefaultClientEndpointMetricManager // decRefCount no need to lock, because that only cause to be expired later. - private long lastNanoTime = System.nanoTime(); + private volatile long lastNanoTime = System.nanoTime(); public DefaultClientEndpointMetric(String address) { super(address); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java index bdd5a1e52f8..17977a468ca 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java @@ -50,7 +50,10 @@ public DefaultClientEndpointMetricManager(MetricsOptionsEx metricsOptionsEx) { public DefaultClientEndpointMetric getOrCreateEndpointMetric(String address) { rwlock.readLock().lock(); try { - return clientEndpointMetricMap.computeIfAbsent(address, DefaultClientEndpointMetric::new); + if (clientEndpointMetricMap.get(address) == null) { + clientEndpointMetricMap.put(address, new DefaultClientEndpointMetric(address)); + } + return clientEndpointMetricMap.get(address); } finally { rwlock.readLock().unlock(); } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpParser.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpParser.java index a59004d62a1..4e79f69fca3 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpParser.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpParser.java @@ -19,16 +19,10 @@ import java.nio.charset.StandardCharsets; -import io.netty.buffer.ByteBuf; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; import io.vertx.core.parsetools.RecordParser; -/** - * TcpParser - * - * - */ public class TcpParser implements Handler { public static final byte[] TCP_MAGIC; @@ -86,16 +80,17 @@ public boolean firstNEqual(byte[] a, byte[] b, int n) { protected void onParse(Buffer buffer) { switch (status) { case TCP_HEADER: - ByteBuf buf = buffer.getByteBuf(); - if (!firstNEqual(TCP_MAGIC, buf.array(), TCP_MAGIC.length)) { + if (!firstNEqual(TCP_MAGIC, buffer.getBytes(), TCP_MAGIC.length)) { reset(); return; } - buf.skipBytes(TCP_MAGIC.length); - msgId = buf.readLong(); - totalLen = buf.readInt(); - headerLen = buf.readInt(); + int index = TCP_MAGIC.length; + msgId = buffer.getLong(index); + index = index + 8; + totalLen = buffer.getInt(index); + index = index + 4; + headerLen = buffer.getInt(index); if (headerLen > totalLen || headerLen <= 0) { throw new IllegalStateException("possibly attack."); } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServer.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServer.java index 46608d1212c..8a960175ad8 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServer.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServer.java @@ -19,8 +19,8 @@ import java.net.InetSocketAddress; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; -import org.apache.servicecomb.foundation.common.utils.ExceptionUtils; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.foundation.ssl.SSLOptionFactory; @@ -31,6 +31,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.net.NetServer; import io.vertx.core.net.NetServerOptions; @@ -49,10 +50,10 @@ public void init(Vertx vertx, String sslKey, AsyncResultCallback LOGGER.error("Unexpected error in server.{}", ExceptionUtils.getExceptionMessageWithoutTrace(e))); + netServer.exceptionHandler( + e -> LOGGER.error("Unexpected error in server.", e)); InetSocketAddress socketAddress = endpointObject.getSocketAddress(); - netServer.listen(socketAddress.getPort(), socketAddress.getHostString(), ar -> { - if (ar.succeeded()) { + Future result = netServer.listen(socketAddress.getPort(), socketAddress.getHostString()); + result.onComplete((s, f) -> { + if (f == null) { callback.success(socketAddress); return; } // 监听失败 String msg = String.format("listen failed, address=%s", socketAddress); - callback.fail(new Exception(msg, ar.cause())); + callback.fail(new Exception(msg, f)); }); } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferInputStream.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferInputStream.java index 58938cf9823..af0d4acf185 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferInputStream.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferInputStream.java @@ -20,58 +20,69 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; - -import io.netty.buffer.ByteBuf; +import io.vertx.core.buffer.Buffer; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; public class BufferInputStream extends ServletInputStream { - private final ByteBuf byteBuf; + private int readIndex = 0; + + private final Buffer byteBuf; - public BufferInputStream(ByteBuf buffer) { + public BufferInputStream(Buffer buffer) { this.byteBuf = buffer; } @Override public long skip(long len) { int skipLen = Math.min((int) len, available()); - byteBuf.skipBytes(skipLen); + this.readIndex += skipLen; return skipLen; } public byte readByte() { - return byteBuf.readByte(); + int index = readIndex; + readIndex = index + 1; + return byteBuf.getByte(index); } @Override public int read() { - return byteBuf.readUnsignedByte(); + return this.readByte() & 255; } public boolean readBoolean() { - return byteBuf.readBoolean(); + return this.readByte() != 0; } public short readShort() { - return byteBuf.readShort(); + int index = readIndex; + readIndex = index + 2; + return byteBuf.getShort(index); } public int readInt() { - return byteBuf.readInt(); + int index = readIndex; + readIndex = index + 4; + return byteBuf.getInt(index); } public long readLong() { - return byteBuf.readLong(); + int index = readIndex; + readIndex = index + 8; + return byteBuf.getLong(index); } public int getIndex() { - return byteBuf.readerIndex(); + return readIndex; } public String readString() { int length = readInt(); byte[] bytes = new byte[length]; - byteBuf.readBytes(bytes); + int index = readIndex; + readIndex = index + length; + byteBuf.getBytes(index, readIndex, bytes); return new String(bytes, StandardCharsets.UTF_8); } @@ -83,40 +94,47 @@ public int read(byte[] b) { @Override public int read(byte[] b, int off, int len) { int avail = available(); - if (len > avail) { - len = avail; + if (avail <= 0) { + return -1; } if (len == 0) { - return -1; + return 0; } - byteBuf.readBytes(b, off, len); + if (len > avail) { + len = avail; + } + + int index = readIndex; + readIndex = index + len; + + byteBuf.getBytes(index, readIndex, b); return len; } @Override public int available() { - return byteBuf.readableBytes(); + return byteBuf.length() - readIndex; } @Override public void close() { - byteBuf.release(); + // nothing to do } @Override public void reset() throws IOException { - byteBuf.resetReaderIndex(); + readIndex = 0; } - public ByteBuf getByteBuf() { + public Buffer getByteBuf() { return byteBuf; } @Override public boolean isFinished() { - return !byteBuf.isReadable(); + return byteBuf.length() > readIndex; } @Override diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferOutputStream.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferOutputStream.java index f063d5d0656..42c9b31261d 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferOutputStream.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferOutputStream.java @@ -20,9 +20,7 @@ import java.io.OutputStream; import java.nio.charset.StandardCharsets; -import io.netty.buffer.ByteBuf; import io.vertx.core.buffer.Buffer; -import io.vertx.core.buffer.impl.VertxByteBufAllocator; /** * BufferOutputStream. @@ -32,40 +30,36 @@ public class BufferOutputStream extends OutputStream { private static final int DIRECT_BUFFER_SIZE = 1024; - protected ByteBuf byteBuf; + protected Buffer byteBuf; public BufferOutputStream() { - this(VertxByteBufAllocator.DEFAULT.heapBuffer(DIRECT_BUFFER_SIZE, Integer.MAX_VALUE)); + this(Buffer.buffer(DIRECT_BUFFER_SIZE)); } - public BufferOutputStream(ByteBuf buffer) { + public BufferOutputStream(Buffer buffer) { this.byteBuf = buffer; } - public ByteBuf getByteBuf() { - return byteBuf; - } - public Buffer getBuffer() { - return Buffer.buffer(byteBuf); + return byteBuf; } public int length() { - return byteBuf.readableBytes(); + return byteBuf.length(); } public void writeByte(byte value) { - byteBuf.writeByte(value); + byteBuf.appendByte(value); } // 实际是写byte @Override public void write(int byteValue) { - byteBuf.writeByte((byte) byteValue); + byteBuf.appendByte((byte) byteValue); } public void write(boolean value) { - byteBuf.writeBoolean(value); + byteBuf.appendByte(value ? (byte) 1 : (byte) 0); } public void writeInt(int pos, int value) { @@ -73,20 +67,20 @@ public void writeInt(int pos, int value) { } public void writeShort(short value) { - byteBuf.writeShort(value); + byteBuf.appendShort(value); } public void writeInt(int value) { - byteBuf.writeInt(value); + byteBuf.appendInt(value); } public void writeLong(long value) { - byteBuf.writeLong(value); + byteBuf.appendLong(value); } public void writeString(String value) { - byteBuf.writeInt(value.length()); - byteBuf.writeCharSequence(value, StandardCharsets.UTF_8); + writeInt(value.length()); + byteBuf.appendString(value, StandardCharsets.UTF_8.toString()); } @Override @@ -96,7 +90,7 @@ public void write(byte[] b) { @Override public void write(byte[] bytes, int offset, int len) { - byteBuf.writeBytes(bytes, offset, len); + byteBuf.appendBytes(bytes, offset, len); } @Override @@ -105,6 +99,6 @@ public void close() { } public int writerIndex() { - return byteBuf.writerIndex(); + return byteBuf.length(); } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/InputStreamToReadStream.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/InputStreamToReadStream.java index 8ca0a6a4ee5..580d460e5c0 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/InputStreamToReadStream.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/InputStreamToReadStream.java @@ -19,12 +19,11 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.netty.buffer.Unpooled; -import io.vertx.core.AsyncResult; import io.vertx.core.Context; import io.vertx.core.Handler; import io.vertx.core.Promise; @@ -40,7 +39,7 @@ public class InputStreamToReadStream implements ReadStream { private final InputStream inputStream; - private boolean closed; + private volatile boolean closed; private boolean paused; @@ -97,26 +96,29 @@ public synchronized InputStreamToReadStream handler(Handler handler) { } class ReadResult { - int readed; + int read; byte[] bytes = new byte[readBufferSize]; void doRead() throws IOException { - readed = inputStream.read(bytes); + read = inputStream.read(bytes); } Buffer toBuffer() { - return Buffer.buffer(Unpooled.wrappedBuffer(bytes).writerIndex(readed)); + return Buffer.buffer(Arrays.copyOf(bytes, read)); } } private synchronized void doRead() { if (!readInProgress) { readInProgress = true; - - context.executeBlocking(this::readInWorker, - true, - this::afterReadInEventloop); + Promise future = Promise.promise(); + context.executeBlocking(() -> { + readInWorker(future); + return null; + }, + true); + future.future().onComplete(this::afterReadInEventloop); } } @@ -135,15 +137,14 @@ public void handleException(Throwable e) { exceptionHandler.handle(e); } - private synchronized void afterReadInEventloop(AsyncResult ar) { - if (ar.failed()) { - handleException(ar.cause()); + private synchronized void afterReadInEventloop(ReadResult readResult, Throwable failure) { + if (failure != null) { + handleException(failure); return; } readInProgress = false; - ReadResult readResult = ar.result(); - if (readResult.readed < 0) { + if (readResult.read < 0) { handleEnd(); return; } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/OutputStreamToWriteStream.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/OutputStreamToWriteStream.java index 95ea0b5076d..35681a723a8 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/OutputStreamToWriteStream.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/OutputStreamToWriteStream.java @@ -21,10 +21,10 @@ import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.foundation.common.io.AsyncCloseable; -import io.vertx.core.AsyncResult; import io.vertx.core.Context; import io.vertx.core.Future; import io.vertx.core.Handler; @@ -60,7 +60,7 @@ public class OutputStreamToWriteStream implements WriteStream, AsyncClos // we just need to flow control by pump, so use another size private final Queue buffers = new ConcurrentLinkedQueue<>(); - private int currentBufferCount; + private final AtomicInteger currentBufferCount = new AtomicInteger(); // just indicate if buffers is full, not control add logic // must >= SMALLEST_MAX_BUFFERS @@ -92,22 +92,20 @@ private void handleException(Throwable t) { @Override public synchronized Future write(Buffer data) { Promise result = Promise.promise(); - write(data, ar -> { - if (ar.failed()) { - handleException(ar.cause()); - } - result.complete(); - }); + write(data, result); return result.future(); } - @Override - public void write(Buffer data, Handler> handler) { - currentBufferCount++; + private void write(Buffer data, Promise future) { + currentBufferCount.incrementAndGet(); buffers.add(data); - context.executeBlocking(this::writeInWorker, - true, - handler); + context.executeBlocking( + () -> { + writeInWorker(future); + return null; + }, + true + ); } protected void writeInWorker(Promise future) { @@ -122,12 +120,13 @@ protected void writeInWorker(Promise future) { outputStream.write(buffer.getBytes()); synchronized (OutputStreamToWriteStream.this) { - currentBufferCount--; - Runnable action = (currentBufferCount == 0 && closedDeferred != null) ? closedDeferred : this::checkDrained; + currentBufferCount.decrementAndGet(); + Runnable action = (currentBufferCount.get() == 0 && closedDeferred != null) + ? closedDeferred : this::checkDrained; action.run(); } } catch (IOException e) { - currentBufferCount--; + currentBufferCount.decrementAndGet(); future.fail(e); return; } @@ -135,8 +134,8 @@ protected void writeInWorker(Promise future) { } @Override - public void end(Handler> handler) { - close(); + public Future end() { + return Future.fromCompletionStage(close()); } @Override @@ -148,7 +147,7 @@ public WriteStream setWriteQueueMaxSize(int maxSize) { @Override public synchronized boolean writeQueueFull() { - return currentBufferCount >= maxBuffers; + return currentBufferCount.get() >= maxBuffers; } @Override @@ -158,7 +157,7 @@ public synchronized WriteStream drainHandler(Handler handler) { } private synchronized void checkDrained() { - if (drainHandler != null && currentBufferCount <= drainMark) { + if (drainHandler != null && currentBufferCount.get() <= drainMark) { Handler handler = drainHandler; drainHandler = null; handler.handle(null); @@ -186,7 +185,7 @@ private synchronized CompletableFuture closeInternal() { closed = true; CompletableFuture future = new CompletableFuture<>(); - if (currentBufferCount == 0) { + if (currentBufferCount.get() == 0) { doClose(future); } else { closedDeferred = () -> doClose(future); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpCommon.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpCommon.java index 5b70f72766e..ae4b5bac727 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpCommon.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpCommon.java @@ -20,27 +20,23 @@ import org.apache.servicecomb.foundation.common.io.AsyncCloseable; -import io.vertx.core.Context; +import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientResponse; -import io.vertx.core.streams.Pump; import io.vertx.core.streams.ReadStream; import io.vertx.core.streams.WriteStream; +import io.vertx.core.streams.impl.PipeImpl; public class PumpCommon { /** - * - * @param context - * @param readStream - * @param writeStream * @return future of save action
*

important: *

if writeStream is AsyncCloseable, future means write complete *

if writeStream is not AsyncCloseable, future only means read complete */ @SuppressWarnings("unchecked") - public CompletableFuture pump(Context context, ReadStream readStream, WriteStream writeStream, + public CompletableFuture pump(ReadStream readStream, WriteStream writeStream, Handler throwableHandler) { CompletableFuture readFuture = new CompletableFuture<>(); @@ -62,17 +58,18 @@ public CompletableFuture pump(Context context, ReadStream readStre readFuture.completeExceptionally(e); }); readStream.exceptionHandler(readFuture::completeExceptionally); - // just means read finished, not means write finished - readStream.endHandler(readFuture::complete); - // if readStream(HttpClientResponse) and writeStream(HttpServerResponse) - // belongs to difference eventloop - // maybe will cause deadlock - // if happened, vertx will print deadlock stacks - Pump.pump(readStream, writeStream).start(); - context.runOnContext(v -> readStream.resume()); + Future pipeResult = new PipeImpl<>(readStream).endOnComplete(false).to(writeStream); - if (!AsyncCloseable.class.isInstance(writeStream)) { + pipeResult.onComplete((s) -> readFuture.complete(null), + (f) -> { + if (throwableHandler != null) { + throwableHandler.handle(f); + } + readFuture.completeExceptionally(f); + }); + + if (!(writeStream instanceof AsyncCloseable)) { return readFuture; } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java index 07a43549f52..2c5284e12dc 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java @@ -21,7 +21,7 @@ import java.io.OutputStream; import java.util.concurrent.CompletableFuture; -import javax.servlet.http.Part; +import jakarta.servlet.http.Part; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.foundation.vertx.http.DownloadUtils; @@ -69,7 +69,7 @@ private CompletableFuture> prepareReadStream() { public CompletableFuture toWriteStream(WriteStream writeStream, Handler throwableHandler) { return prepareReadStream() - .thenCompose(readStream -> new PumpCommon().pump(context, readStream, writeStream, throwableHandler)) + .thenCompose(readStream -> new PumpCommon().pump(readStream, writeStream, throwableHandler)) .whenComplete((v, e) -> { if (e != null) { LOGGER.error("to write stream failed.", e); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConnection.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConnection.java index 95f460013d2..fcc22bfd7c9 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConnection.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConnection.java @@ -20,9 +20,6 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicLong; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.CompositeByteBuf; import io.vertx.core.Context; import io.vertx.core.buffer.Buffer; import io.vertx.core.net.NetSocket; @@ -49,7 +46,7 @@ public class TcpConnection { // so this optimization: // 1.avoid vertx's lock // 2.reduce netty's task schedule - private final Queue writeQueue = new ConcurrentLinkedQueue<>(); + private final Queue writeQueue = new ConcurrentLinkedQueue<>(); private final AtomicLong writeQueueSize = new AtomicLong(); @@ -80,10 +77,10 @@ public NetSocket getNetSocket() { public void initNetSocket(NetSocketImpl netSocket) { this.netSocket = netSocket; - this.context = netSocket.getContext(); + this.context = netSocket.context(); } - public void write(ByteBuf buf) { + public void write(Buffer buf) { writeQueue.add(buf); long oldSize = writeQueueSize.getAndIncrement(); if (oldSize == 0) { @@ -97,25 +94,15 @@ protected void scheduleWrite() { } protected void writeInContext() { - CompositeByteBuf cbb = ByteBufAllocator.DEFAULT.compositeBuffer(); for (; ; ) { - ByteBuf buf = writeQueue.poll(); + Buffer buf = writeQueue.poll(); if (buf == null) { break; } writeQueueSize.decrementAndGet(); - cbb.addComponent(true, buf); - if (cbb.numComponents() == cbb.maxNumComponents()) { - CompositeByteBuf last = cbb; - netSocket.write(Buffer.buffer(last)).onComplete(r -> last.release()); - cbb = ByteBufAllocator.DEFAULT.compositeBuffer(); - } - } - if (cbb.isReadable()) { - CompositeByteBuf last = cbb; - netSocket.write(Buffer.buffer(last)).onComplete(r -> last.release()); + netSocket.write(buf); } } } diff --git a/foundations/foundation-vertx/src/main/resources/META-INF/spring/cse.bean.xml b/foundations/foundation-vertx/src/main/resources/META-INF/spring/cse.bean.xml deleted file mode 100644 index 908d9ce1944..00000000000 --- a/foundations/foundation-vertx/src/main/resources/META-INF/spring/cse.bean.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - diff --git a/foundations/foundation-vertx/src/test/java/io/vertx/ext/web/impl/TestHttpServerRequestUtils.java b/foundations/foundation-vertx/src/test/java/io/vertx/ext/web/impl/TestHttpServerRequestUtils.java deleted file mode 100644 index ec8255fdcc9..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/vertx/ext/web/impl/TestHttpServerRequestUtils.java +++ /dev/null @@ -1,45 +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 io.vertx.ext.web.impl; - -import io.vertx.ext.web.RequestBody; -import org.apache.servicecomb.foundation.vertx.http.VertxServerRequestToHttpServletRequest; - -import io.vertx.core.http.impl.HttpServerRequestInternal; -import io.vertx.ext.web.AllowForwardHeaders; -import io.vertx.ext.web.RoutingContext; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -// HttpServerRequestWrapper is a package visible class, so put this test in package io.vertx.ext.web.impl -public class TestHttpServerRequestUtils { - @Test - public void testVertxServerRequestToHttpServletRequest() { - RoutingContext context = Mockito.mock(RoutingContext.class); - HttpServerRequestInternal request = Mockito.mock(HttpServerRequestInternal.class); - HttpServerRequestWrapper wrapper = new HttpServerRequestWrapper(request, AllowForwardHeaders.NONE); - Mockito.when(request.scheme()).thenReturn("http"); - Mockito.when(context.request()).thenReturn(wrapper); - RequestBody requestBody = Mockito.mock(RequestBody.class); - Mockito.when(context.body()).thenReturn(requestBody); - - VertxServerRequestToHttpServletRequest reqEx = new VertxServerRequestToHttpServletRequest(context, "abc"); - Assertions.assertEquals("abc", reqEx.getRequestURI()); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestAddressResolverConfig.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestAddressResolverConfig.java deleted file mode 100644 index a8ce06a33cb..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestAddressResolverConfig.java +++ /dev/null @@ -1,106 +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.servicecomb.foundation.vertx; - -import java.util.Arrays; -import java.util.Collections; - -import org.apache.commons.configuration.Configuration; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; - -import io.vertx.core.dns.AddressResolverOptions; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -public class TestAddressResolverConfig { - - @BeforeAll - public static void classSetup() { - ArchaiusUtils.resetConfig(); - } - - @AfterAll - public static void classTeardown() { - ArchaiusUtils.resetConfig(); - } - - @Test - public void testGetResolverFromResource() { - Configuration finalConfig = Mockito.mock(Configuration.class); - ArchaiusUtils.resetConfig(); - ArchaiusUtils.setProperty("addressResolver.servers", "8.8.8.8,8.8.4.4"); - - Mockito.when(finalConfig.getStringArray("addressResolver.servers")).thenReturn(new String[] {"6.6.6.6", "6.6.4.4"}); - Mockito.when(finalConfig.getStringArray("addressResolver.searchDomains")).thenReturn(new String[] {"default.svc.local.cluster"}); - Mockito.when(finalConfig.getInteger("addressResolver.queryTimeout", null)).thenReturn(2000); - Mockito.when(finalConfig.getInteger("addressResolver.maxQueries", null)).thenReturn(-2); - - AddressResolverOptions resolverOptions = AddressResolverConfig.getAddressResover("test", finalConfig); - Assertions.assertEquals(Arrays.asList("6.6.6.6", "6.6.4.4"), resolverOptions.getServers()); - Assertions.assertEquals(Collections.singletonList("default.svc.local.cluster"), resolverOptions.getSearchDomains()); - Assertions.assertEquals(resolverOptions.getQueryTimeout(), - 2000); - Assertions.assertNotEquals(resolverOptions.getMaxQueries(), - -2); - } - - @Test - public void testGetResolver() { - ArchaiusUtils.resetConfig(); - ArchaiusUtils.setProperty("addressResolver.servers", "8.8.8.8,8.8.4.4"); - ArchaiusUtils.setProperty("addressResolver.optResourceEnabled", true); - ArchaiusUtils.setProperty("addressResolver.cacheMinTimeToLive", 0); - ArchaiusUtils.setProperty("addressResolver.cacheMaxTimeToLive", 10000); - ArchaiusUtils.setProperty("addressResolver.cacheNegativeTimeToLive", 0); - ArchaiusUtils.setProperty("addressResolver.queryTimeout", 1000); - ArchaiusUtils.setProperty("addressResolver.maxQueries", 3); - ArchaiusUtils.setProperty("addressResolver.test.maxQueries", 3); - ArchaiusUtils.setProperty("addressResolver.rdFlag", true); - ArchaiusUtils.setProperty("addressResolver.searchDomains", - "default.svc.local.cluster,svc.local.cluster,local.cluster"); - ArchaiusUtils.setProperty("addressResolver.test.searchDomains", - "test.svc.local.cluster,svc.local.cluster,local.cluster"); - ArchaiusUtils.setProperty("addressResolver.ndots", 3); - ArchaiusUtils.setProperty("addressResolver.rotateServers", true); - AddressResolverOptions aroc = AddressResolverConfig.getAddressResover("test"); - Assertions.assertEquals(Arrays.asList("8.8.8.8", "8.8.4.4"), aroc.getServers()); - Assertions.assertEquals(Arrays.asList("test.svc.local.cluster", "svc.local.cluster", "local.cluster"), - aroc.getSearchDomains()); - AddressResolverOptions aroc1 = AddressResolverConfig.getAddressResover("test1"); - Assertions.assertEquals(Arrays.asList("default.svc.local.cluster", "svc.local.cluster", "local.cluster"), - aroc1.getSearchDomains()); - Assertions.assertTrue(aroc.isOptResourceEnabled()); - } - - @Test - public void testGetResolverDefault() { - ArchaiusUtils.resetConfig(); - ArchaiusUtils.setProperty("addressResolver.servers", "8.8.8.8,8.8.4.4"); - ArchaiusUtils.setProperty("addressResolver.maxQueries", 3); - ArchaiusUtils.setProperty("addressResolver.rdFlag", false); - AddressResolverOptions resolverOptions = AddressResolverConfig.getAddressResover("test"); - Assertions.assertEquals(Arrays.asList("8.8.8.8", "8.8.4.4"), resolverOptions.getServers()); - Assertions.assertEquals(3, resolverOptions.getMaxQueries()); - Assertions.assertEquals(Integer.MAX_VALUE, resolverOptions.getCacheMaxTimeToLive()); - Assertions.assertFalse(resolverOptions.isOptResourceEnabled()); - Assertions.assertNull(resolverOptions.getSearchDomains()); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSharedVertxFactory.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSharedVertxFactory.java index 176285b71be..8e77a256251 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSharedVertxFactory.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSharedVertxFactory.java @@ -16,15 +16,34 @@ */ package org.apache.servicecomb.foundation.vertx; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import io.vertx.core.impl.SysProps; public class TestSharedVertxFactory { + Environment environment = Mockito.mock(Environment.class); + + @BeforeEach + public void setUp() { + Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) + .thenReturn(true); + LegacyPropertyFactory.setEnvironment(environment); + } + + @Test public void getTransportVertx() { - Assertions.assertNotNull(SharedVertxFactory.getSharedVertx()); - Assertions.assertSame(SharedVertxFactory.getSharedVertx(), SharedVertxFactory.getSharedVertx()); + Assertions.assertNotNull(SharedVertxFactory.getSharedVertx(environment)); + Assertions.assertSame(SharedVertxFactory.getSharedVertx(environment), + SharedVertxFactory.getSharedVertx(environment)); - SharedVertxFactory.getSharedVertx().close(); + SharedVertxFactory.getSharedVertx(environment).close(); } } diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSimpleBodyHandler.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSimpleBodyHandler.java index 76ede3d9636..8aec460f9c0 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSimpleBodyHandler.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSimpleBodyHandler.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.foundation.vertx; -import javax.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status; import org.junit.After; import org.junit.Before; diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestStream.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestStream.java index 2c9fdc895cc..af3e2078c90 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestStream.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestStream.java @@ -22,8 +22,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import io.netty.buffer.ByteBuf; -import io.vertx.core.buffer.impl.VertxByteBufAllocator; +import io.vertx.core.buffer.Buffer; public class TestStream { @@ -31,16 +30,21 @@ public class TestStream { @Test public void testBufferInputStream() { - ByteBuf obuf = VertxByteBufAllocator.DEFAULT.heapBuffer(DIRECT_BUFFER_SIZE, Integer.MAX_VALUE); - obuf.writeBytes(("testss").getBytes()); + Buffer obuf = Buffer.buffer(DIRECT_BUFFER_SIZE); + obuf.appendBytes(("tests1").getBytes()); @SuppressWarnings("resource") BufferInputStream oBufferInputStream = new BufferInputStream(obuf); - Assertions.assertNotEquals(1234, oBufferInputStream.skip(0)); - Assertions.assertNotEquals(obuf.readByte(), oBufferInputStream.readByte()); - Assertions.assertEquals(obuf.readByte() + 1, oBufferInputStream.read()); - Assertions.assertEquals(obuf.readBoolean(), oBufferInputStream.readBoolean()); - Assertions.assertEquals(obuf.readerIndex(), oBufferInputStream.getIndex()); - Assertions.assertEquals(obuf.readableBytes(), oBufferInputStream.available()); + + byte test = oBufferInputStream.readByte(); + Assertions.assertEquals((byte) 't', test); + Assertions.assertEquals((byte) 'e', oBufferInputStream.read()); + + Assertions.assertEquals(2, oBufferInputStream.skip(2)); + Assertions.assertEquals((byte) 's', oBufferInputStream.read()); + Assertions.assertTrue(oBufferInputStream.readBoolean()); + + Assertions.assertEquals(6, oBufferInputStream.getIndex()); + Assertions.assertEquals(0, oBufferInputStream.available()); } @Test @@ -53,9 +57,9 @@ public void testBufferOutputStream() { Assertions.assertTrue((1 < oBufferOutputStream.length())); @SuppressWarnings("resource") - BufferInputStream oBufferInputStream = new BufferInputStream(oBufferOutputStream.getByteBuf()); + BufferInputStream oBufferInputStream = new BufferInputStream(oBufferOutputStream.getBuffer()); Assertions.assertEquals("test", oBufferInputStream.readString()); Assertions.assertEquals(1, oBufferInputStream.readByte()); - Assertions.assertEquals(true, oBufferInputStream.readBoolean()); + Assertions.assertTrue(oBufferInputStream.readBoolean()); } } diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxTLSBuilder.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxTLSBuilder.java index 9ed503df3b9..f99abd698df 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxTLSBuilder.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxTLSBuilder.java @@ -17,35 +17,33 @@ package org.apache.servicecomb.foundation.vertx; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.foundation.ssl.SSLOptionFactory; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import org.junit.Before; import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; import io.vertx.core.http.ClientAuth; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpServerOptions; import mockit.Mock; import mockit.MockUp; -import org.junit.jupiter.api.Assertions; public class TestVertxTLSBuilder { - @BeforeClass - public static void classSetup() { - ArchaiusUtils.resetConfig(); - } + Environment environment = Mockito.mock(Environment.class); - @AfterClass - public static void classTeardown() { - ArchaiusUtils.resetConfig(); + @Before + public void setUp() { + LegacyPropertyFactory.setEnvironment(environment); } @Test public void testBuildHttpServerOptions() { - SSLOption option = SSLOption.buildFromYaml("rest.provider"); + SSLOption option = SSLOption.build("rest.provider", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpServerOptions serverOptions = new HttpServerOptions(); VertxTLSBuilder.buildNetServerOptions(option, custom, serverOptions); @@ -78,7 +76,8 @@ public SSLOption createSSLOption() { @Test public void testBuildHttpClientOptions_ssl_withFactory() { - ArchaiusUtils.setProperty("ssl.exist.sslOptionFactory", SSLOptionFactoryForTest.class.getName()); + Mockito.when(environment.getProperty("ssl.exist.sslOptionFactory")) + .thenReturn(SSLOptionFactoryForTest.class.getName()); HttpClientOptions clientOptions = new HttpClientOptions(); VertxTLSBuilder.buildHttpClientOptions("exist", clientOptions); Assertions.assertTrue(clientOptions.isSsl()); @@ -87,7 +86,7 @@ public void testBuildHttpClientOptions_ssl_withFactory() { @Test public void testBuildHttpClientOptions() { - SSLOption option = SSLOption.buildFromYaml("rest.consumer"); + SSLOption option = SSLOption.build("rest.consumer", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpClientOptions serverOptions = new HttpClientOptions(); VertxTLSBuilder.buildHttpClientOptions(option, custom, serverOptions); @@ -97,7 +96,7 @@ public void testBuildHttpClientOptions() { @Test public void testBuildClientOptionsBase() { - SSLOption option = SSLOption.buildFromYaml("rest.consumer"); + SSLOption option = SSLOption.build("rest.consumer", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpClientOptions serverOptions = new HttpClientOptions(); VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); @@ -107,7 +106,7 @@ public void testBuildClientOptionsBase() { @Test public void testBuildClientOptionsBaseFileNull() { - SSLOption option = SSLOption.buildFromYaml("rest.consumer"); + SSLOption option = SSLOption.build("rest.consumer", environment); option.setKeyStore(null); option.setTrustStore(null); option.setCrl(null); @@ -120,7 +119,7 @@ public void testBuildClientOptionsBaseFileNull() { @Test public void testBuildClientOptionsBaseAuthPeerFalse() { - SSLOption option = SSLOption.buildFromYaml("rest.consumer"); + SSLOption option = SSLOption.build("rest.consumer", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpClientOptions serverOptions = new HttpClientOptions(); new MockUp() { @@ -137,7 +136,7 @@ public boolean isAuthPeer() { @Test public void testBuildClientOptionsBaseSTORE_JKS() { - SSLOption option = SSLOption.buildFromYaml("rest.consumer"); + SSLOption option = SSLOption.build("rest.consumer", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpClientOptions serverOptions = new HttpClientOptions(); new MockUp() { @@ -154,7 +153,7 @@ public String getKeyStoreType() { @Test public void testBuildClientOptionsBaseSTORE_PKCS12() { - SSLOption option = SSLOption.buildFromYaml("rest.consumer"); + SSLOption option = SSLOption.build("rest.consumer", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpClientOptions serverOptions = new HttpClientOptions(); new MockUp() { @@ -171,7 +170,7 @@ public String getTrustStoreType() { @Test public void testBuildHttpServerOptionsRequest() { - SSLOption option = SSLOption.buildFromYaml("rest.provider"); + SSLOption option = SSLOption.build("rest.provider", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpServerOptions serverOptions = new HttpServerOptions(); diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxUtils.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxUtils.java index 813e01525ec..9f966cbcbde 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxUtils.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxUtils.java @@ -23,24 +23,37 @@ import java.io.InputStream; import java.util.concurrent.CountDownLatch; -import io.vertx.core.file.impl.FileResolverImpl; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.foundation.common.Holder; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.buffer.Buffer; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; +import io.vertx.core.impl.SysProps; public class TestVertxUtils { + Environment environment = Mockito.mock(Environment.class); + + @BeforeEach + public void setUp() { + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) + .thenReturn(true); + + LegacyPropertyFactory.setEnvironment(environment); + } + @Test public void testGetOrCreateVertx() throws InterruptedException { - Vertx vertx = VertxUtils.getOrCreateVertxByName("ut", null); + Vertx vertx = VertxUtils.getOrCreateVertxByName("ut", null, null); Holder name = new Holder<>(); CountDownLatch latch = new CountDownLatch(1); @@ -56,29 +69,26 @@ public void testGetOrCreateVertx() throws InterruptedException { @Test public void testCreateVertxWithFileCPResolving() { - // Prepare - ArchaiusUtils.resetConfig(); - // create .vertx folder - ArchaiusUtils.setProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, false); + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) + .thenReturn(false); deleteCacheFile(); - VertxUtils.getOrCreateVertxByName("testCreateVertxWithFileCPResolvingFalse", null); + VertxUtils.getOrCreateVertxByName("testCreateVertxWithFileCPResolvingFalse", null, null); Assertions.assertTrue(isCacheFileExists()); VertxUtils.blockCloseVertxByName("testCreateVertxWithFileCPResolvingFalse"); // don't create .vertx folder deleteCacheFile(); Assertions.assertFalse(isCacheFileExists()); - ArchaiusUtils.setProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, true); - VertxUtils.getOrCreateVertxByName("testCreateVertxWithFileCPResolvingTrue", null); + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) + .thenReturn(true); + VertxUtils.getOrCreateVertxByName("testCreateVertxWithFileCPResolvingTrue", null, null); Assertions.assertFalse(isCacheFileExists()); VertxUtils.blockCloseVertxByName("testCreateVertxWithFileCPResolvingTrue"); - - ArchaiusUtils.resetConfig(); } private void deleteCacheFile() { - String cacheDirBase = System.getProperty(FileResolverImpl.CACHE_DIR_BASE_PROP_NAME, + String cacheDirBase = System.getProperty(SysProps.FILE_CACHE_DIR.name, System.getProperty("java.io.tmpdir", ".")); File folder = new File(cacheDirBase); File[] files = folder.listFiles(); @@ -90,7 +100,7 @@ private void deleteCacheFile() { } private boolean isCacheFileExists() { - String cacheDirBase = System.getProperty(FileResolverImpl.CACHE_DIR_BASE_PROP_NAME, + String cacheDirBase = System.getProperty(SysProps.FILE_CACHE_DIR.name, System.getProperty("java.io.tmpdir", ".")); File folder = new File(cacheDirBase); File[] files = folder.listFiles(); @@ -104,7 +114,7 @@ private boolean isCacheFileExists() { @Test public void testVertxUtilsInitNullOptions() { - Vertx vertx = VertxUtils.init(null, null); + Vertx vertx = VertxUtils.init(null, null, null); Assertions.assertNotEquals(null, vertx); VertxUtils.blockCloseVertx(vertx); } @@ -113,7 +123,7 @@ public void testVertxUtilsInitNullOptions() { public void testVertxUtilsInitWithOptions() { VertxOptions oOptions = new VertxOptions(); - Vertx vertx = VertxUtils.init(null, oOptions); + Vertx vertx = VertxUtils.init(null, oOptions, null); Assertions.assertNotEquals(null, vertx); VertxUtils.blockCloseVertx(vertx); } @@ -121,11 +131,12 @@ public void testVertxUtilsInitWithOptions() { @Test public void testgetBytesFastBufferInputStream() throws IOException { byte[] bytes = new byte[] {1}; - ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes); + Buffer byteBuf = Buffer.buffer(bytes); try (BufferInputStream inputStream = new BufferInputStream(byteBuf)) { byte[] result = VertxUtils.getBytesFast(inputStream); - Assertions.assertSame(bytes, result); + Assertions.assertEquals(bytes.length, result.length); + Assertions.assertEquals(bytes[0], result[0]); } } diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/http/TestHttpClientPoolFactory.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/http/TestHttpClientPoolFactory.java deleted file mode 100644 index cf57fe619d1..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/http/TestHttpClientPoolFactory.java +++ /dev/null @@ -1,45 +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.servicecomb.foundation.vertx.client.http; - -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.impl.ContextInternal; -import io.vertx.core.impl.VertxInternal; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -public class TestHttpClientPoolFactory { - private final HttpClientOptions httpClientOptions = new HttpClientOptions(); - - HttpClientPoolFactory factory = new HttpClientPoolFactory(httpClientOptions); - - @Test - public void createClientPool() { - VertxInternal vertx = Mockito.mock(VertxInternal.class); - ContextInternal context = Mockito.mock(ContextInternal.class); - HttpClient httpClient = Mockito.mock(HttpClient.class); - Mockito.when(context.owner()).thenReturn(vertx); - Mockito.when(vertx.createHttpClient(httpClientOptions)).thenReturn(httpClient); - - HttpClientWithContext pool = factory.createClientPool(context); - - Assertions.assertSame(context, pool.context()); - Assertions.assertSame(httpClient, pool.getHttpClient()); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestNetClientWrapper.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestNetClientWrapper.java deleted file mode 100644 index adbcd3e8138..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestNetClientWrapper.java +++ /dev/null @@ -1,108 +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.servicecomb.foundation.vertx.client.tcp; - - -import java.util.ArrayList; -import java.util.List; - -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.Before; -import org.junit.Test; - -import io.vertx.core.AsyncResult; -import io.vertx.core.Handler; -import io.vertx.core.Promise; -import io.vertx.core.Vertx; -import io.vertx.core.net.NetClient; -import io.vertx.core.net.NetSocket; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; -import org.junit.jupiter.api.Assertions; - -public class TestNetClientWrapper { - @Mocked - Vertx vertx; - - @Mocked - TcpClientConfig normalClientConfig; - - @Mocked - NetClient normalNetClient; - - @Mocked - TcpClientConfig sslClientConfig; - - @Mocked - NetClient sslNetClient; - - NetClientWrapper netClientWrapper; - - @Before - public void setup() { - new Expectations() { - { - vertx.createNetClient(normalClientConfig); - result = normalNetClient; - vertx.createNetClient(sslClientConfig); - result = sslNetClient; - } - }; - netClientWrapper = new NetClientWrapper(vertx, normalClientConfig, sslClientConfig); - } - - @Test - public void getClientConfig() { - Assertions.assertSame(normalClientConfig, netClientWrapper.getClientConfig(false)); - Assertions.assertSame(sslClientConfig, netClientWrapper.getClientConfig(true)); - } - - @Test - public void connect(@Mocked NetSocket normalSocket, @Mocked NetSocket sslSocket) { - int port = 8000; - String host = "localhost"; - - Promise promiseConnect = Promise.promise(); - new MockUp(normalNetClient) { - @Mock - NetClient connect(int port, String host, Handler> connectHandler) { - promiseConnect.complete(normalSocket); - connectHandler.handle(promiseConnect.future()); - return null; - } - }; - - Promise sslPromiseConnect = Promise.promise(); - new MockUp(sslNetClient) { - @Mock - NetClient connect(int port, String host, Handler> connectHandler) { - sslPromiseConnect.complete(sslSocket); - connectHandler.handle(sslPromiseConnect.future()); - return null; - } - }; - - List socks = new ArrayList<>(); - netClientWrapper.connect(false, port, host, asyncSocket -> socks.add(asyncSocket.result())); - netClientWrapper.connect(true, port, host, asyncSocket -> socks.add(asyncSocket.result())); - - MatcherAssert.assertThat(socks, Matchers.contains(normalSocket, sslSocket)); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnection.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnection.java index 0767898f2d4..f072d962000 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnection.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnection.java @@ -25,13 +25,14 @@ import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; import org.junit.Before; import org.junit.Test; +import org.junit.jupiter.api.Assertions; import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.vertx.core.AsyncResult; import io.vertx.core.Context; +import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Promise; +import io.vertx.core.buffer.Buffer; import io.vertx.core.net.NetSocket; import io.vertx.core.net.impl.NetSocketImpl; import mockit.Deencapsulation; @@ -39,7 +40,6 @@ import mockit.Mock; import mockit.MockUp; import mockit.Mocked; -import org.junit.jupiter.api.Assertions; public class TestTcpClientConnection { @Mocked @@ -90,14 +90,14 @@ public void send_inWorkingStatus(@Mocked AbstractTcpClientPackage tcpClientPacka Deencapsulation.setField(tcpClientConnection, "status", Status.WORKING); long msgId = 1; - ByteBuf byteBuf = Unpooled.buffer(); + Buffer byteBuf = Buffer.buffer(); new Expectations(tcpClientConnection) { { tcpClientPackage.getMsgId(); result = msgId; tcpClientPackage.createStream(); result = tcpOutputStream; - tcpOutputStream.getByteBuf(); + tcpOutputStream.getBuffer(); result = byteBuf; } }; @@ -172,7 +172,7 @@ public void send_disconnectedToWorking(@Mocked AbstractTcpClientPackage tcpClien { tcpClientPackage.getMsgId(); result = msgId; - tcpClientConnection.write((ByteBuf) any); + tcpClientConnection.write((Buffer) any); } }; new MockUp(context) { @@ -195,9 +195,9 @@ public void connect_success(@Mocked NetSocketImpl netSocket) { Promise promise = Promise.promise(); new MockUp(netClientWrapper) { @Mock - void connect(boolean ssl, int port, String host, Handler> connectHandler) { + public Future connect(boolean ssl, int port, String host) { promise.complete(netSocket); - connectHandler.handle(promise.future()); + return promise.future(); } }; @@ -216,9 +216,9 @@ public void connect_failed() { RuntimeException error = new RuntimeExceptionWithoutStackTrace(); new MockUp(netClientWrapper) { @Mock - void connect(boolean ssl, int port, String host, Handler> connectHandler) { + public Future connect(boolean ssl, int port, String host) { promise.fail(error); - connectHandler.handle(promise.future()); + return promise.future(); } }; diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnectionPool.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnectionPool.java deleted file mode 100644 index dfaf8ec60a2..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnectionPool.java +++ /dev/null @@ -1,43 +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.servicecomb.foundation.vertx.client.tcp; - -import io.vertx.core.impl.ContextInternal; -import io.vertx.core.impl.VertxInternal; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -public class TestTcpClientConnectionPool { - - TcpClientConnectionPool pool; - - @BeforeEach - public void setup() { - ContextInternal context = Mockito.mock(ContextInternal.class); - VertxInternal vertx = Mockito.mock(VertxInternal.class); - Mockito.when(context.owner()).thenReturn(vertx); - NetClientWrapper netClientWrapper = Mockito.mock(NetClientWrapper.class); - pool = new TcpClientConnectionPool(context, netClientWrapper); - } - - @Test - public void create() { - Assertions.assertTrue(pool.create("rest://localhost:8765") instanceof TcpClientConnection); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletRequest.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletRequest.java index cf1b0bfd4b8..de7c8dfa1db 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletRequest.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletRequest.java @@ -19,13 +19,13 @@ import java.util.Collections; -import javax.servlet.http.HttpServletRequest; - import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; import org.junit.jupiter.api.Assertions; +import jakarta.servlet.http.HttpServletRequest; + public class TestAbstractHttpServletRequest { HttpServletRequest request = new AbstractHttpServletRequest() { }; @@ -175,13 +175,6 @@ public void testGetRequestDispatcher() { checkError(error); } - @SuppressWarnings("deprecation") - @Test - public void testGetRealPath() { - Error error = Assertions.assertThrows(Error.class, () -> request.getRealPath("")); - checkError(error); - } - @Test public void testGetRemotePort() { Error error = Assertions.assertThrows(Error.class, () -> request.getRemotePort()); @@ -398,13 +391,6 @@ public void testIsRequestedSessionIdFromURL() { checkError(error); } - @SuppressWarnings("deprecation") - @Test - public void testIsRequestedSessionIdFromUrl() { - Error error = Assertions.assertThrows(Error.class, () -> request.isRequestedSessionIdFromUrl()); - checkError(error); - } - @Test public void testAuthenticate() { Error error = Assertions.assertThrows(Error.class, () -> request.authenticate(null)); diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java index 9b85cb1c5d4..053cc56e7de 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java @@ -91,8 +91,7 @@ public void testGetBufferSize() { @Test public void testFlushBuffer() { - Error error = Assertions.assertThrows(Error.class, () -> response.flushBuffer()); - checkError(error); + Assertions.assertDoesNotThrow(() -> response.flushBuffer()); } @Test @@ -149,20 +148,6 @@ public void testEncodeRedirectURL() { checkError(error); } - @SuppressWarnings("deprecation") - @Test - public void testEncodeUrl() { - Error error = Assertions.assertThrows(Error.class, () -> response.encodeUrl(null)); - checkError(error); - } - - @SuppressWarnings("deprecation") - @Test - public void testEncodeRedirectUrl() { - Error error = Assertions.assertThrows(Error.class, () -> response.encodeRedirectUrl(null)); - checkError(error); - } - @Test public void testSendErrorScAndMsg() { Error error = Assertions.assertThrows(Error.class, () -> response.sendError(0, null)); @@ -223,13 +208,6 @@ public void testSetStatusSc() { checkError(error); } - @SuppressWarnings("deprecation") - @Test - public void testSetStatusScAndSm() { - Error error = Assertions.assertThrows(Error.class, () -> response.setStatus(0, "")); - checkError(error); - } - @Test public void testGetStatus() { Error error = Assertions.assertThrows(Error.class, () -> response.getStatus()); diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestFileUploadPart.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestFileUploadPart.java index f7d132ed397..ed2e331da71 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestFileUploadPart.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestFileUploadPart.java @@ -20,8 +20,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.UUID; import org.apache.commons.io.FileUtils; @@ -33,8 +33,8 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - +import org.mockito.Mockito; + public class TestFileUploadPart { FileUpload fileUpload; diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestReadStreamPart.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestReadStreamPart.java deleted file mode 100644 index baaa331ac8b..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestReadStreamPart.java +++ /dev/null @@ -1,200 +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.servicecomb.foundation.vertx.http; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.UUID; -import java.util.concurrent.ExecutionException; - -import javax.ws.rs.core.HttpHeaders; - -import org.apache.commons.io.FileUtils; -import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; -import org.apache.servicecomb.foundation.vertx.stream.InputStreamToReadStream; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; - -import io.vertx.core.Handler; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.file.FileSystemException; -import io.vertx.core.file.OpenOptions; -import io.vertx.core.http.HttpClientResponse; -import io.vertx.core.impl.SyncContext; -import io.vertx.core.impl.VertxInternal; -import io.vertx.core.streams.WriteStream; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestReadStreamPart { - static Vertx vertx = Vertx.vertx(); - - static SyncContext context = new SyncContext(); - - static String src = "src"; - - static InputStream inputStream = new ByteArrayInputStream(src.getBytes()); - - InputStreamToReadStream readStream = new InputStreamToReadStream(context, inputStream, true); - - ReadStreamPart part = new ReadStreamPart(context, readStream); - - @Before - public void setup() throws IOException { - context.setOwner((VertxInternal) vertx); - inputStream.reset(); - } - - @AfterClass - public static void teardown() { - vertx.close(); - } - - @Test - public void constructFromHttpClientResponse_noContentType(@Mocked HttpClientResponse httpClientResponse) { - new Expectations() { - { - httpClientResponse.getHeader(HttpHeaders.CONTENT_DISPOSITION); - result = "xx;filename=name.txt"; - httpClientResponse.getHeader(HttpHeaders.CONTENT_TYPE); - result = null; - } - }; - - part = new ReadStreamPart(context, httpClientResponse); - - Assertions.assertEquals("name.txt", part.getSubmittedFileName()); - Assertions.assertEquals("text/plain", part.getContentType()); - } - - @Test - public void constructFromHttpClientResponse_hasContentType(@Mocked HttpClientResponse httpClientResponse) { - new Expectations() { - { - httpClientResponse.getHeader(HttpHeaders.CONTENT_DISPOSITION); - result = "xx;filename=name.txt"; - httpClientResponse.getHeader(HttpHeaders.CONTENT_TYPE); - result = "type"; - } - }; - - part = new ReadStreamPart(context, httpClientResponse); - - Assertions.assertEquals("name.txt", part.getSubmittedFileName()); - Assertions.assertEquals("type", part.getContentType()); - } - - @Test - public void saveToWriteStream() throws InterruptedException, ExecutionException { - Buffer buf = Buffer.buffer(); - WriteStream writeStream = new MockUp>() { - @Mock - WriteStream write(Buffer data) { - buf.appendBuffer(data); - return null; - } - }.getMockInstance(); - - part.saveToWriteStream(writeStream).get(); - - Assertions.assertEquals(src, buf.toString()); - } - - @Test - public void saveToWriteStream_writeException() throws InterruptedException, ExecutionException { - RuntimeException error = new RuntimeExceptionWithoutStackTrace(); - WriteStream writeStream = new MockUp>() { - Handler exceptionHandler; - - @Mock - WriteStream exceptionHandler(Handler handler) { - this.exceptionHandler = handler; - return null; - } - - @Mock - WriteStream write(Buffer data) { - exceptionHandler.handle(error); - return null; - } - }.getMockInstance(); - - ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> part.saveToWriteStream(writeStream).get()); - Assertions.assertTrue(exception.getCause() instanceof RuntimeException); - } - - @Test - public void saveToWrite_readException(@Mocked WriteStream writeStream) { - RuntimeException error = new RuntimeExceptionWithoutStackTrace(); - new MockUp(inputStream) { - @Mock - int read(byte[] b) throws IOException { - throw error; - } - }; - - ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> part.saveToWriteStream(writeStream).get()); - Assertions.assertTrue(exception.getCause() instanceof RuntimeException); - } - - @Test - public void saveAsBytes() throws InterruptedException, ExecutionException { - Assertions.assertArrayEquals(src.getBytes(), part.saveAsBytes().get()); - } - - @Test - public void saveAsString() throws InterruptedException, ExecutionException { - Assertions.assertEquals(src, part.saveAsString().get()); - } - - @Test - public void saveToFile() throws InterruptedException, ExecutionException, IOException { - File dir = new File("target/notExist-" + UUID.randomUUID()); - File file = new File(dir, "a.txt"); - - Assertions.assertFalse(dir.exists()); - - part.saveToFile(file.getAbsolutePath()).get(); - - Assertions.assertEquals(src, FileUtils.readFileToString(file, StandardCharsets.UTF_8)); - - FileUtils.forceDelete(dir); - Assertions.assertFalse(dir.exists()); - } - - @Test - public void saveToFile_notExist_notCreate() throws InterruptedException, ExecutionException, IOException { - File dir = new File("target/notExist-" + UUID.randomUUID()); - File file = new File(dir, "a.txt"); - - Assertions.assertFalse(dir.exists()); - - ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> { - OpenOptions openOptions = new OpenOptions().setCreateNew(false); - part.saveToFile(file, openOptions).get(); - }); - Assertions.assertTrue(exception.getCause() instanceof FileSystemException); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletRequestEx.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletRequestEx.java index 299dbf6f5ad..15e52aa5f71 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletRequestEx.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletRequestEx.java @@ -25,10 +25,10 @@ import java.util.Locale; import java.util.Map; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.core.MediaType; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.HttpMethod; +import jakarta.ws.rs.core.MediaType; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; @@ -117,7 +117,7 @@ public void parameterMap_merge() throws IOException { inherited.put("p1", v1); Buffer buffer = Buffer.buffer("p1=v1-3;p2=v2"); - BufferInputStream inputStream = new BufferInputStream(buffer.getByteBuf()); + BufferInputStream inputStream = new BufferInputStream(buffer); new Expectations() { { request.getParameterMap(); diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.java index 51363b3a4bb..289d5b15226 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.java @@ -21,12 +21,7 @@ import java.io.IOException; import java.io.InputStream; -import javax.servlet.ServletOutputStream; -import javax.servlet.WriteListener; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.Part; - -import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.apache.servicecomb.foundation.common.part.InputStreamPart; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.junit.Before; @@ -35,6 +30,10 @@ import org.mockito.Mockito; import io.vertx.core.buffer.Buffer; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.WriteListener; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.Part; public class TestStandardHttpServletResponseEx { @@ -74,14 +73,6 @@ public void getBodyBytesLength() { Assertions.assertEquals(3, responseEx.getBodyBytesLength()); } - @Test - public void setStatus() { - responseEx.setStatus(200, "ok"); - Assertions.assertEquals(200, responseEx.getStatus()); - Assertions.assertEquals(200, responseEx.getStatusType().getStatusCode()); - Assertions.assertEquals("ok", responseEx.getStatusType().getReasonPhrase()); - } - @Test public void flushBuffer() throws IOException { Buffer buffer = Buffer.buffer(); @@ -112,7 +103,7 @@ public void write(int b) { Buffer body = Buffer.buffer().appendString("body"); responseEx.setBodyBuffer(body); responseEx.flushBuffer(); - Assertions.assertEquals("body", buffer.toString()); + Assertions.assertEquals(0, buffer.length()); } @Test diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxClientRequestToHttpServletRequest.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxClientRequestToHttpServletRequest.java index 881c1b6de23..9cbe78713b3 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxClientRequestToHttpServletRequest.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxClientRequestToHttpServletRequest.java @@ -19,7 +19,7 @@ import java.util.Collections; -import javax.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.HttpHeaders; import org.apache.servicecomb.foundation.common.http.HttpUtils; import org.hamcrest.MatcherAssert; diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxClientResponseToHttpServletResponse.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxClientResponseToHttpServletResponse.java index 1969851f75a..ca204c9b6fc 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxClientResponseToHttpServletResponse.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxClientResponseToHttpServletResponse.java @@ -17,8 +17,8 @@ package org.apache.servicecomb.foundation.vertx.http; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.Response.StatusType; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response.StatusType; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerRequestToHttpServletRequest.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerRequestToHttpServletRequest.java index 7be1f187aa8..9d53cd649da 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerRequestToHttpServletRequest.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerRequestToHttpServletRequest.java @@ -24,10 +24,10 @@ import java.util.Map; import java.util.Set; -import javax.servlet.AsyncContext; -import javax.servlet.ServletInputStream; -import javax.servlet.http.Cookie; -import javax.ws.rs.core.HttpHeaders; +import jakarta.servlet.AsyncContext; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.Cookie; +import jakarta.ws.rs.core.HttpHeaders; import io.vertx.ext.web.RequestBody; import io.vertx.ext.web.impl.RoutingContextInternal; diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java deleted file mode 100644 index cf6d5aa95b0..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java +++ /dev/null @@ -1,419 +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.servicecomb.foundation.vertx.http; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import javax.servlet.http.Part; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.Response.StatusType; - -import org.apache.commons.io.FileUtils; -import org.apache.servicecomb.foundation.common.http.HttpStatus; -import org.apache.servicecomb.foundation.common.part.FilePart; -import org.apache.servicecomb.foundation.vertx.stream.PumpFromPart; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.Before; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; - -import io.vertx.core.AsyncResult; -import io.vertx.core.Context; -import io.vertx.core.Handler; -import io.vertx.core.MultiMap; -import io.vertx.core.Promise; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpServerResponse; -import io.vertx.core.impl.SyncContext; -import io.vertx.core.streams.WriteStream; -import mockit.Deencapsulation; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestVertxServerResponseToHttpServletResponse { - MultiMap headers = MultiMap.caseInsensitiveMultiMap(); - - HttpStatus httpStatus = new HttpStatus(123, "default"); - - HttpServerResponse serverResponse; - - VertxServerResponseToHttpServletResponse response; - - boolean flushWithBody; - - boolean runOnContextInvoked; - - @Mocked - Vertx vertx; - - @Mocked - Context context; - - boolean chunked; - - @Before - public void setup() { - serverResponse = new MockUp() { - @Mock - HttpServerResponse setStatusCode(int statusCode) { - Deencapsulation.setField(httpStatus, "statusCode", statusCode); - return serverResponse; - } - - @Mock - HttpServerResponse setStatusMessage(String statusMessage) { - Deencapsulation.setField(httpStatus, "reason", statusMessage); - return serverResponse; - } - - @Mock - int getStatusCode() { - return httpStatus.getStatusCode(); - } - - @Mock - String getStatusMessage() { - return httpStatus.getReasonPhrase(); - } - - @Mock - MultiMap headers() { - return headers; - } - - @Mock - HttpServerResponse putHeader(String name, String value) { - headers.set(name, value); - return serverResponse; - } - - @Mock - void end() { - flushWithBody = false; - } - - @Mock - void end(Buffer chunk) { - flushWithBody = true; - } - - @Mock - HttpServerResponse setChunked(boolean chunked) { - TestVertxServerResponseToHttpServletResponse.this.chunked = chunked; - return serverResponse; - } - - @Mock - boolean isChunked() { - return chunked; - } - }.getMockInstance(); - - new Expectations() { - { - Vertx.currentContext(); - result = context; - } - }; - - new MockUp(context) { - @Mock - void runOnContext(Handler action) { - runOnContextInvoked = true; - action.handle(null); - } - - @Mock - void executeBlocking(Handler> blockingCodeHandler, boolean ordered, - Handler> resultHandler) { - SyncContext.syncExecuteBlocking(blockingCodeHandler, resultHandler); - } - - @Mock - Vertx owner() { - return vertx; - } - }; - - response = new VertxServerResponseToHttpServletResponse(serverResponse); - } - - @Test - public void construct_invalid() throws IOException { - new Expectations() { - { - Vertx.currentContext(); - result = null; - } - }; - - NullPointerException exception = Assertions.assertThrows(NullPointerException.class, () -> new VertxServerResponseToHttpServletResponse(serverResponse)); - Assertions.assertEquals("must run in vertx context.", exception.getMessage()); - } - - @Test - public void setContentType() { - response.setContentType("json"); - Assertions.assertEquals("json", headers.get(HttpHeaders.CONTENT_TYPE)); - } - - @SuppressWarnings("deprecation") - @Test - public void setStatus() { - response.setStatus(222, "test"); - Assertions.assertEquals(222, httpStatus.getStatusCode()); - Assertions.assertEquals("test", httpStatus.getReasonPhrase()); - } - - @Test - public void getStatusType() { - StatusType status = response.getStatusType(); - - Assertions.assertSame(status, response.getStatusType()); - Assertions.assertEquals(123, httpStatus.getStatusCode()); - Assertions.assertEquals("default", httpStatus.getReasonPhrase()); - } - - @Test - public void addHeader() { - response.addHeader("n1", "v1_1"); - response.addHeader("n1", "v1_2"); - response.addHeader("n2", "v2"); - - Assertions.assertEquals(2, headers.size()); - MatcherAssert.assertThat(headers.getAll("n1"), Matchers.contains("v1_1", "v1_2")); - MatcherAssert.assertThat(headers.getAll("n2"), Matchers.contains("v2")); - } - - @Test - public void setHeader() { - response.setHeader("n1", "v1_1"); - response.setHeader("n1", "v1_2"); - response.setHeader("n2", "v2"); - - Assertions.assertEquals(2, headers.size()); - MatcherAssert.assertThat(headers.getAll("n1"), Matchers.contains("v1_2")); - MatcherAssert.assertThat(headers.getAll("n2"), Matchers.contains("v2")); - } - - @Test - public void getStatus() { - Assertions.assertEquals(123, response.getStatus()); - } - - @Test - public void getContentType() { - headers.set(HttpHeaders.CONTENT_TYPE, "json"); - Assertions.assertEquals("json", response.getContentType()); - } - - @Test - public void getHeader() { - headers.set(HttpHeaders.CONTENT_TYPE, "json"); - Assertions.assertEquals("json", response.getHeader(HttpHeaders.CONTENT_TYPE)); - } - - @Test - public void getHeaders() { - headers.add("h1", "h1_1"); - headers.add("h1", "h1_2"); - - MatcherAssert.assertThat(response.getHeaders("h1"), Matchers.contains("h1_1", "h1_2")); - } - - @Test - public void getHeaderNames() { - headers.add("h1", "h1"); - headers.add("h2", "h2"); - - MatcherAssert.assertThat(response.getHeaderNames(), Matchers.contains("h1", "h2")); - } - - @Test - public void flushBuffer_sameContext() throws IOException { - response.flushBuffer(); - - Assertions.assertFalse(runOnContextInvoked); - } - - @Test - public void flushBuffer_diffContext() throws IOException { - new Expectations() { - { - Vertx.currentContext(); - result = null; - } - }; - response.flushBuffer(); - - Assertions.assertTrue(runOnContextInvoked); - } - - @Test - public void internalFlushBufferNoBody() throws IOException { - response.internalFlushBuffer(); - - Assertions.assertFalse(flushWithBody); - } - - @Test - public void internalFlushBufferWithBody() throws IOException { - response.setBodyBuffer(Buffer.buffer()); - response.internalFlushBuffer(); - - Assertions.assertTrue(flushWithBody); - } - - @Test - public void prepareSendPartHeader_update(@Mocked Part part) { - new Expectations() { - { - part.getContentType(); - result = "type"; - part.getSubmittedFileName(); - result = "测 试"; - } - }; - DownloadUtils.prepareDownloadHeader(response, part); - - Assertions.assertTrue(serverResponse.isChunked()); - Assertions.assertEquals("type", response.getHeader(HttpHeaders.CONTENT_TYPE)); - Assertions.assertEquals( - "attachment;filename=%E6%B5%8B%20%20%20%20%20%E8%AF%95;filename*=utf-8''%E6%B5%8B%20%20%20%20%20%E8%AF%95", - response.getHeader(HttpHeaders.CONTENT_DISPOSITION)); - } - - @Test - public void prepareSendPartHeader_notUpdate(@Mocked Part part) { - headers.add(HttpHeaders.CONTENT_LENGTH, "10"); - headers.add(HttpHeaders.CONTENT_TYPE, "type"); - headers.add(HttpHeaders.CONTENT_DISPOSITION, "disposition"); - - DownloadUtils.prepareDownloadHeader(response, part); - - Assertions.assertFalse(serverResponse.isChunked()); - Assertions.assertEquals("type", response.getHeader(HttpHeaders.CONTENT_TYPE)); - Assertions.assertEquals("disposition", response.getHeader(HttpHeaders.CONTENT_DISPOSITION)); - } - - @Test - public void sendPart_openInputStreamFailed(@Mocked Part part) - throws IOException { - IOException ioException = new IOException("forbid open stream"); - new Expectations() { - { - part.getInputStream(); - result = ioException; - } - }; - - CompletableFuture future = response.sendPart(part); - - ExecutionException exception = Assertions.assertThrows(ExecutionException.class, future::get); - Assertions.assertTrue(exception.getCause() instanceof IOException); - } - - @Test - public void sendPart_testPartIsNull(@Mocked Part part) throws InterruptedException, ExecutionException { - CompletableFuture future1 = response.sendPart(null); - Assertions.assertNull(future1.get()); - } - - @Test - public void sendPart_inputStreamBreak(@Mocked Part part, @Mocked InputStream inputStream) - throws IOException { - IOException ioException = new IOException("forbid read"); - new Expectations() { - { - part.getInputStream(); - result = inputStream; - inputStream.read((byte[]) any); - result = ioException; - } - }; - - CompletableFuture future = response.sendPart(part); - - ExecutionException exception = Assertions.assertThrows(ExecutionException.class, future::get); - Assertions.assertTrue(exception.getCause() instanceof IOException); - } - - @Test - public void sendPart_ReadStreamPart(@Mocked ReadStreamPart part) { - CompletableFuture future = new CompletableFuture<>(); - new MockUp() { - @Mock - CompletableFuture toWriteStream(WriteStream writeStream, Handler throwableHandler) { - return future; - } - }; - - Assertions.assertSame(future, response.sendPart(part)); - } - - @Test - public void sendPart_succ(@Mocked Part part, @Mocked InputStream inputStream) - throws IOException, InterruptedException, ExecutionException { - new Expectations() { - { - part.getInputStream(); - result = inputStream; - inputStream.read((byte[]) any); - result = -1; - } - }; - - CompletableFuture future = response.sendPart(part); - - Assertions.assertNull(future.get()); - } - - @Test - public void clearPartResource_deleteFile() throws IOException { - File file = new File("target", UUID.randomUUID() + ".txt"); - FileUtils.write(file, "content", StandardCharsets.UTF_8); - FilePart part = new FilePart(null, file).setDeleteAfterFinished(true); - - Assertions.assertTrue(file.exists()); - DownloadUtils.clearPartResource(part); - Assertions.assertFalse(file.exists()); - } - - @Test - public void clearPartResource_notDeleteFile() throws IOException { - File file = new File("target", UUID.randomUUID() + ".txt"); - FileUtils.write(file, "content", StandardCharsets.UTF_8); - FilePart part = new FilePart(null, file); - - Assertions.assertTrue(file.exists()); - DownloadUtils.clearPartResource(part); - Assertions.assertTrue(file.exists()); - - file.delete(); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultVertxMetricsFactory.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultVertxMetricsFactory.java index 1ae56ebee4a..ccc67374c82 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultVertxMetricsFactory.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultVertxMetricsFactory.java @@ -29,14 +29,12 @@ public class TestDefaultVertxMetricsFactory { DefaultVertxMetricsFactory factory = new DefaultVertxMetricsFactory(); - @SuppressWarnings("deprecation") @Test public void metrics() { MetricsOptions metricsOptions = factory.newOptions(); options.setMetricsOptions(metricsOptions); VertxMetrics vertxMetrics = factory.metrics(options); - Assertions.assertSame(factory, metricsOptions.getFactory()); Assertions.assertTrue(metricsOptions.isEnabled()); Assertions.assertSame(factory.getVertxMetrics(), vertxMetrics); diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpServer.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpServer.java deleted file mode 100644 index 2e1446c9e73..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpServer.java +++ /dev/null @@ -1,147 +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.servicecomb.foundation.vertx.server; - -import java.net.InetSocketAddress; - -import org.apache.servicecomb.foundation.common.net.URIEndpointObject; -import org.apache.servicecomb.foundation.vertx.AsyncResultCallback; -import org.apache.servicecomb.foundation.vertx.metrics.DefaultTcpServerMetrics; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; -import org.junit.Test; - -import io.vertx.core.Handler; -import io.vertx.core.Vertx; -import io.vertx.core.net.NetServer; -import io.vertx.core.net.NetServerOptions; -import io.vertx.core.net.NetSocket; -import io.vertx.core.net.impl.NetSocketImpl; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; -import org.junit.jupiter.api.Assertions; - -public class TestTcpServer { - static class TcpServerForTest extends TcpServer { - public TcpServerForTest(URIEndpointObject endpointObject) { - super(endpointObject); - } - - @Override - protected TcpServerConnection createTcpServerConnection() { - return new TcpServerConnection() { - @Override - public void init(NetSocket netSocket) { - super.init(netSocket); - } - }; - } - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - @Test - public void testTcpServerNonSSL(@Mocked Vertx vertx, @Mocked AsyncResultCallback callback, - @Mocked NetServer netServer) { - new Expectations() { - { - vertx.createNetServer(); - result = netServer; - netServer.connectHandler((Handler) any); - netServer.listen(anyInt, anyString, (Handler) any); - } - }; - URIEndpointObject endpointObject = new URIEndpointObject("highway://127.0.0.1:6663"); - TcpServer server = new TcpServerForTest(endpointObject); - // assert done in Expectations - server.init(vertx, "", callback); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - @Test - public void testTcpServerSSL(@Mocked Vertx vertx, @Mocked AsyncResultCallback callback, - @Mocked NetServer netServer) { - new Expectations() { - { - vertx.createNetServer((NetServerOptions) any); - result = netServer; - netServer.connectHandler((Handler) any); - netServer.listen(anyInt, anyString, (Handler) any); - } - }; - URIEndpointObject endpointObject = new URIEndpointObject("highway://127.0.0.1:6663?sslEnabled=true"); - TcpServer server = new TcpServerForTest(endpointObject); - // assert done in Expectations - server.init(vertx, "", callback); - } - - Handler connectHandler; - - boolean netSocketClosed; - - @SuppressWarnings({"rawtypes", "unchecked"}) - @Test - public void testConnectionLimit(@Mocked Vertx vertx, @Mocked AsyncResultCallback callback, - @Mocked NetServer netServer, @Mocked NetSocketImpl netSocket) { - DefaultServerEndpointMetric endpointMetric = new DefaultServerEndpointMetric(null); - DefaultTcpServerMetrics tcpServerMetrics = new DefaultTcpServerMetrics(endpointMetric); - - new MockUp(netServer) { - @Mock - NetServer connectHandler(Handler handler) { - connectHandler = handler; - return netServer; - } - }; - new MockUp(netSocket) { - @Mock - void close() { - netSocketClosed = true; - } - }; - new Expectations() { - { - vertx.createNetServer((NetServerOptions) any); - result = netServer; - netServer.listen(anyInt, anyString, (Handler) any); - netSocket.metrics(); - result = tcpServerMetrics; - } - }; - URIEndpointObject endpointObject = new URIEndpointObject("highway://127.0.0.1:6663?sslEnabled=true"); - TcpServer server = new TcpServerForTest(endpointObject) { - @Override - protected int getConnectionLimit() { - return 2; - } - }; - // assert done in Expectations - server.init(vertx, "", callback); - - // no problem - endpointMetric.onConnect(); - endpointMetric.onConnect(); - connectHandler.handle(netSocket); - - // reject - endpointMetric.onConnect(); - connectHandler.handle(netSocket); - Assertions.assertTrue(netSocketClosed); - Assertions.assertEquals(1, endpointMetric.getRejectByConnectionLimitCount()); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestBufferInputStream.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestBufferInputStream.java index 070fe207d6d..40be5d48642 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestBufferInputStream.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestBufferInputStream.java @@ -29,8 +29,7 @@ import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; +import io.vertx.core.buffer.Buffer; public class TestBufferInputStream { @@ -38,7 +37,7 @@ public class TestBufferInputStream { @Before public void setUp() throws Exception { - ByteBuf buffer = Mockito.mock(ByteBuf.class); + Buffer buffer = Mockito.mock(Buffer.class); instance = new BufferInputStream(buffer); } @@ -60,8 +59,8 @@ public void testReadDecorate() throws IOException { gzipOutputStream.write(text.getBytes()); gzipOutputStream.close(); - ByteBuf buffer = Unpooled.buffer(); - buffer.writeBytes(out.toByteArray()); + Buffer buffer = Buffer.buffer(); + buffer.appendBytes(out.toByteArray()); out.close(); BufferInputStream bufferInputStream = new BufferInputStream(buffer); GZIPInputStream gzipInputStream = new GZIPInputStream(bufferInputStream); diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestPumpFromPart.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestPumpFromPart.java deleted file mode 100644 index 1a88defcf63..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestPumpFromPart.java +++ /dev/null @@ -1,172 +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.servicecomb.foundation.vertx.stream; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.concurrent.ExecutionException; - -import javax.servlet.http.Part; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.RandomStringUtils; -import org.apache.servicecomb.foundation.common.part.InputStreamPart; -import org.apache.servicecomb.foundation.vertx.stream.InputStreamToReadStream.ReadResult; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.Test; - -import io.vertx.core.Context; -import io.vertx.core.Promise; -import io.vertx.core.impl.SyncContext; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import org.junit.jupiter.api.Assertions; - -public class TestPumpFromPart { - String src = RandomStringUtils.random(100, true, true); - - boolean inputStreamClosed; - - InputStream inputStream = new ByteArrayInputStream(src.getBytes()) { - @Override - public void close() throws IOException { - super.close(); - inputStreamClosed = true; - } - }; - - Part part; - - boolean outputStreamClosed; - - BufferOutputStream outputStream; - - IOException error = new IOException(); - - Context context = new SyncContext(); - - private void run(Context context, boolean closeOutput) throws Throwable { - inputStream.reset(); - part = new InputStreamPart("name", inputStream); - - outputStream = new BufferOutputStream() { - @Override - public void close() { - super.close(); - outputStreamClosed = true; - } - }; - - new PumpFromPart(context, part).toOutputStream(outputStream, closeOutput).get(); - } - - public void do_pump_succ(Context context) throws Throwable { - run(context, true); - - Assertions.assertEquals(src, outputStream.getBuffer().toString()); - Assertions.assertTrue(inputStreamClosed); - Assertions.assertTrue(outputStreamClosed); - } - - @Test - public void pump_succ() throws Throwable { - do_pump_succ(null); - do_pump_succ(context); - } - - public void do_pump_outputNotClose(Context context) throws Throwable { - run(context, false); - - Assertions.assertEquals(src, outputStream.getBuffer().toString()); - Assertions.assertFalse(outputStreamClosed); - } - - @Test - public void pump_outputNotClose() throws Throwable { - do_pump_outputNotClose(null); - do_pump_outputNotClose(context); - } - - public void pump_error(Context context) { - try { - run(context, true); - Assertions.fail("must throw exception"); - } catch (Throwable e) { - MatcherAssert.assertThat(e, Matchers.instanceOf(ExecutionException.class)); - MatcherAssert.assertThat(e.getCause(), Matchers.sameInstance(error)); - } - - Assertions.assertTrue(inputStreamClosed); - Assertions.assertTrue(outputStreamClosed); - } - - @Test - public void pump_read_error() throws IOException { - new MockUp() { - @Mock - void readInWorker(Promise future) { - future.fail(error); - } - }; - new Expectations(IOUtils.class) { - { - IOUtils.copyLarge((InputStream) any, (OutputStream) any); - result = error; - } - }; - - pump_error(null); - Assertions.assertTrue(inputStreamClosed); - Assertions.assertTrue(outputStreamClosed); - - inputStreamClosed = false; - outputStreamClosed = false; - pump_error(context); - Assertions.assertTrue(inputStreamClosed); - Assertions.assertTrue(outputStreamClosed); - } - - @Test - public void pump_write_error() throws IOException { - new MockUp() { - @Mock - void write(byte[] b) throws IOException { - throw error; - } - }; - new Expectations(IOUtils.class) { - { - IOUtils.copyLarge((InputStream) any, (OutputStream) any); - result = error; - } - }; - - pump_error(null); - Assertions.assertTrue(inputStreamClosed); - Assertions.assertTrue(outputStreamClosed); - - inputStreamClosed = false; - outputStreamClosed = false; - pump_error(context); - Assertions.assertTrue(inputStreamClosed); - Assertions.assertTrue(outputStreamClosed); - } -} diff --git a/foundations/pom.xml b/foundations/pom.xml index ad724a691b6..7b3f55d334a 100644 --- a/foundations/pom.xml +++ b/foundations/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default foundations diff --git a/governance/pom.xml b/governance/pom.xml index 5bcf7560875..3f78c36ade5 100644 --- a/governance/pom.xml +++ b/governance/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default 4.0.0 @@ -46,14 +46,6 @@ io.micrometer micrometer-core - - org.aspectj - aspectjweaver - - - org.aspectj - aspectjrt - org.apache.commons commons-lang3 diff --git a/governance/src/main/java/org/apache/servicecomb/governance/GovernanceCommonConfiguration.java b/governance/src/main/java/org/apache/servicecomb/governance/GovernanceCommonConfiguration.java index bd3929ad60d..b5fd042a723 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/GovernanceCommonConfiguration.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/GovernanceCommonConfiguration.java @@ -42,10 +42,9 @@ import org.apache.servicecomb.governance.marker.operator.PrefixOperator; import org.apache.servicecomb.governance.marker.operator.SuffixOperator; import org.apache.servicecomb.governance.properties.BulkheadProperties; -import org.apache.servicecomb.governance.properties.TimeLimiterProperties; -import org.apache.servicecomb.governance.properties.GovernanceCacheProperties; import org.apache.servicecomb.governance.properties.CircuitBreakerProperties; import org.apache.servicecomb.governance.properties.FaultInjectionProperties; +import org.apache.servicecomb.governance.properties.GovernanceCacheProperties; import org.apache.servicecomb.governance.properties.IdentifierRateLimitProperties; import org.apache.servicecomb.governance.properties.InstanceBulkheadProperties; import org.apache.servicecomb.governance.properties.InstanceIsolationProperties; @@ -54,6 +53,7 @@ import org.apache.servicecomb.governance.properties.MatchProperties; import org.apache.servicecomb.governance.properties.RateLimitProperties; import org.apache.servicecomb.governance.properties.RetryProperties; +import org.apache.servicecomb.governance.properties.TimeLimiterProperties; import org.apache.servicecomb.governance.service.MatchersService; import org.apache.servicecomb.governance.service.MatchersServiceImpl; import org.springframework.beans.factory.ObjectProvider; @@ -66,175 +66,175 @@ public class GovernanceCommonConfiguration { // properties configuration @Bean - public BulkheadProperties bulkheadProperties() { + public BulkheadProperties scbBulkheadProperties() { return new BulkheadProperties(); } @Bean - public InstanceBulkheadProperties instanceBulkheadProperties() { + public InstanceBulkheadProperties scbInstanceBulkheadProperties() { return new InstanceBulkheadProperties(); } @Bean - public CircuitBreakerProperties circuitBreakerProperties() { + public CircuitBreakerProperties scbCircuitBreakerProperties() { return new CircuitBreakerProperties(); } @Bean - public InstanceIsolationProperties instanceIsolationProperties() { + public InstanceIsolationProperties scbInstanceIsolationProperties() { return new InstanceIsolationProperties(); } @Bean - public MatchProperties matchProperties() { + public MatchProperties scbMatchProperties() { return new MatchProperties(); } @Bean - public RateLimitProperties rateLimitProperties() { + public RateLimitProperties scbRateLimitProperties() { return new RateLimitProperties(); } @Bean - public IdentifierRateLimitProperties identifierRateLimitProperties() { + public IdentifierRateLimitProperties scbIdentifierRateLimitProperties() { return new IdentifierRateLimitProperties(); } @Bean - public RetryProperties retryProperties() { + public RetryProperties scbRetryProperties() { return new RetryProperties(); } @Bean - public TimeLimiterProperties timeLimiterProperties() { + public TimeLimiterProperties scbTimeLimiterProperties() { return new TimeLimiterProperties(); } @Bean - public GovernanceCacheProperties cacheProperties() { + public GovernanceCacheProperties scbCacheProperties() { return new GovernanceCacheProperties(); } @Bean - public FaultInjectionProperties faultInjectionProperties() { + public FaultInjectionProperties scbFaultInjectionProperties() { return new FaultInjectionProperties(); } @Bean - public LoadBalanceProperties loadBalanceProperties() { + public LoadBalanceProperties scbLoadBalanceProperties() { return new LoadBalanceProperties(); } @Bean - public MapperProperties mapperProperties() { + public MapperProperties scbMapperProperties() { return new MapperProperties(); } // handlers configuration @Bean - public BulkheadHandler bulkheadHandler(BulkheadProperties bulkheadProperties) { - return new BulkheadHandler(bulkheadProperties); + public BulkheadHandler scbBulkheadHandler(BulkheadProperties scbBulkheadProperties) { + return new BulkheadHandler(scbBulkheadProperties); } @Bean - public InstanceBulkheadHandler instanceBulkheadHandler(InstanceBulkheadProperties instanceBulkheadProperties) { - return new InstanceBulkheadHandler(instanceBulkheadProperties); + public InstanceBulkheadHandler scbInstanceBulkheadHandler(InstanceBulkheadProperties scbInstanceBulkheadProperties) { + return new InstanceBulkheadHandler(scbInstanceBulkheadProperties); } @Bean - public LoadBalanceHandler loadBalanceHandler(LoadBalanceProperties loadBalanceProperties) { + public LoadBalanceHandler scbLoadBalanceHandler(LoadBalanceProperties loadBalanceProperties) { return new LoadBalanceHandler(loadBalanceProperties); } @Bean - public CircuitBreakerHandler circuitBreakerHandler(CircuitBreakerProperties circuitBreakerProperties, + public CircuitBreakerHandler scbCircuitBreakerHandler(CircuitBreakerProperties scbCircuitBreakerProperties, AbstractCircuitBreakerExtension circuitBreakerExtension) { - return new CircuitBreakerHandler(circuitBreakerProperties, circuitBreakerExtension); + return new CircuitBreakerHandler(scbCircuitBreakerProperties, circuitBreakerExtension); } @Bean - public InstanceIsolationHandler instanceIsolationHandler(InstanceIsolationProperties instanceIsolationProperties, + public InstanceIsolationHandler scbInstanceIsolationHandler(InstanceIsolationProperties scbInstanceIsolationProperties, AbstractInstanceIsolationExtension isolationExtension, ObjectProvider meterRegistry) { - return new InstanceIsolationHandler(instanceIsolationProperties, isolationExtension, meterRegistry); + return new InstanceIsolationHandler(scbInstanceIsolationProperties, isolationExtension, meterRegistry); } @Bean - public RateLimitingHandler rateLimitingHandler(RateLimitProperties rateLimitProperties) { + public RateLimitingHandler scbRateLimitingHandler(RateLimitProperties rateLimitProperties) { return new RateLimitingHandler(rateLimitProperties); } @Bean - public IdentifierRateLimitingHandler identifierRateLimitingHandler( + public IdentifierRateLimitingHandler scbIdentifierRateLimitingHandler( IdentifierRateLimitProperties identifierRateLimitProperties) { return new IdentifierRateLimitingHandler(identifierRateLimitProperties); } @Bean - public RetryHandler retryHandler(RetryProperties retryProperties, AbstractRetryExtension retryExtension) { + public RetryHandler scbRetryHandler(RetryProperties retryProperties, AbstractRetryExtension retryExtension) { return new RetryHandler(retryProperties, retryExtension); } @Bean - public TimeLimiterHandler timeLimiterHandler(TimeLimiterProperties timeLimiterProperties) { + public TimeLimiterHandler scbTimeLimiterHandler(TimeLimiterProperties timeLimiterProperties) { return new TimeLimiterHandler(timeLimiterProperties); } @Bean - public GovernanceCacheHandler governanceCacheHandler(GovernanceCacheProperties cacheProperties) { + public GovernanceCacheHandler scbGovernanceCacheHandler(GovernanceCacheProperties cacheProperties) { return new GovernanceCacheHandler(cacheProperties); } @Bean - public FaultInjectionHandler faultInjectionHandler(FaultInjectionProperties faultInjectionProperties) { - return new FaultInjectionHandler(faultInjectionProperties); + public FaultInjectionHandler scbFaultInjectionHandler(FaultInjectionProperties scbFaultInjectionProperties) { + return new FaultInjectionHandler(scbFaultInjectionProperties); } @Bean - public MapperHandler mapperHandler(MapperProperties mapperProperties) { - return new MapperHandler(mapperProperties); + public MapperHandler scbMapperHandler(MapperProperties scbMapperProperties) { + return new MapperHandler(scbMapperProperties); } // request processor @Bean - public RequestProcessor requestProcessor(Map operatorMap) { + public RequestProcessor scbRequestProcessor(Map operatorMap) { return new RequestProcessor(operatorMap); } // matchers @Bean - public MatchersService matchersService(RequestProcessor requestProcessor, MatchProperties matchProperties) { - return new MatchersServiceImpl(requestProcessor, matchProperties); + public MatchersService scbMatchersService(RequestProcessor requestProcessor, MatchProperties scbMatchProperties) { + return new MatchersServiceImpl(requestProcessor, scbMatchProperties); } @Bean - public MatchersManager matchersManager(MatchersService matchersService) { + public MatchersManager scbMatchersManager(MatchersService matchersService) { return new MatchersManager(matchersService); } // operators @Bean - public CompareOperator compareOperator() { + public CompareOperator scbCompareOperator() { return new CompareOperator(); } @Bean - public ContainsOperator containsOperator() { + public ContainsOperator scbContainsOperator() { return new ContainsOperator(); } @Bean - public ExactOperator exactOperator() { + public ExactOperator scbExactOperator() { return new ExactOperator(); } @Bean - public PrefixOperator prefixOperator() { + public PrefixOperator scbPrefixOperator() { return new PrefixOperator(); } @Bean - public SuffixOperator suffixOperator() { + public SuffixOperator scbSuffixOperator() { return new SuffixOperator(); } } diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java index fdeb10a5a59..39082661b24 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java @@ -72,7 +72,7 @@ private Disposable getCircuitBreaker(String key, CircuitBreakerP .minimumNumberOfCalls(policy.getMinimumNumberOfCalls()) .slidingWindowType(policy.getSlidingWindowTypeEnum()) .slidingWindowSize(Integer.parseInt(policy.getSlidingWindowSize())) - .recordException(circuitBreakerExtension::isFailedResult) + .recordException(e -> circuitBreakerExtension.isFailedResult(policy.getRecordFailureStatus(), e)) .recordResult(r -> circuitBreakerExtension.isFailedResult(policy.getRecordFailureStatus(), r)) .build(); CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java index 5be1978dd4b..894dd170e99 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java @@ -35,7 +35,7 @@ import io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics; import io.micrometer.core.instrument.MeterRegistry; -public class InstanceIsolationHandler extends AbstractGovernanceHandler { +public final class InstanceIsolationHandler extends AbstractGovernanceHandler { private static final Logger LOGGER = LoggerFactory.getLogger(InstanceIsolationHandler.class); private final InstanceIsolationProperties instanceIsolationProperties; @@ -103,7 +103,7 @@ private Disposable getCircuitBreaker(String key, CircuitBreakerP .minimumNumberOfCalls(policy.getMinimumNumberOfCalls()) .slidingWindowType(policy.getSlidingWindowTypeEnum()) .slidingWindowSize(Integer.parseInt(policy.getSlidingWindowSize())) - .recordException(isolationExtension::isFailedResult) + .recordException(e -> isolationExtension.isFailedResult(policy.getRecordFailureStatus(), e)) .recordResult(r -> isolationExtension.isFailedResult(policy.getRecordFailureStatus(), r)) .build(); CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java index 8b4c819b05d..f7f2476f667 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java @@ -68,7 +68,7 @@ private Disposable getRetry(String key, RetryPolicy retryPolicy) { RetryConfig config = RetryConfig.custom() .maxAttempts(retryPolicy.getMaxAttempts() + 1) .retryOnResult(response -> retryExtension.isFailedResult(retryPolicy.getRetryOnResponseStatus(), response)) - .retryOnException(retryExtension::isFailedResult) + .retryOnException(exception -> retryExtension.isFailedResult(retryPolicy.getRetryOnResponseStatus(), exception)) .intervalFunction(getIntervalFunction(retryPolicy)) .failAfterMaxAttempts(retryPolicy.isFailAfterMaxAttempts()) .build(); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java index 31ae8eca16f..c07ebe39bdd 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java @@ -29,8 +29,8 @@ import com.google.common.collect.ImmutableMap; -import io.vertx.core.VertxException; import io.netty.handler.ssl.SslHandshakeTimeoutException; +import io.vertx.core.VertxException; public interface FailurePredictor { Map, List> STRICT_RETRIABLE = @@ -49,7 +49,7 @@ public interface FailurePredictor { boolean isFailedResult(List statusList, Object result); - default boolean isFailedResult(Throwable e) { + default boolean isFailedResult(List statusList, Throwable e) { return canRetryForException(STRICT_RETRIABLE, e); } diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java index 2a454e5d11a..e3ac0b2abce 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java @@ -16,6 +16,7 @@ */ package org.apache.servicecomb.governance.marker; +import java.util.Collections; import java.util.Map; import org.springframework.util.LinkedCaseInsensitiveMap; @@ -26,7 +27,14 @@ public class GovernanceRequest implements GovernanceRequestExtractor { * For provider: headers indicates the request headers to me. * For consumer: headers indicates the request headers to the target. */ - private Map headers; + private Map headers = Collections.emptyMap(); + + /** + * Queries with this request, maybe null. + * For provider: Queries indicates the request params to me. + * For consumer: Queries indicates the request params to the target. + */ + private Map queries = Collections.emptyMap(); /** * api path with this request, maybe null. For REST, e.g. /foo/bar; For RPC, e.g. MySchema.sayHello @@ -70,16 +78,31 @@ public String header(String key) { return headers.get(key); } + @Override + public String query(String key) { + return queries.get(key); + } + public Map getHeaders() { return headers; } + public Map getQueries() { + return queries; + } + public void setHeaders(Map headers) { Map temp = new LinkedCaseInsensitiveMap<>(); temp.putAll(headers); this.headers = temp; } + public void setQueries(Map queries) { + Map temp = new LinkedCaseInsensitiveMap<>(); + temp.putAll(queries); + this.queries = temp; + } + @Override public String apiPath() { return apiPath; diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequestExtractor.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequestExtractor.java index 873f78665ed..28beb8e8384 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequestExtractor.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequestExtractor.java @@ -23,6 +23,8 @@ public interface GovernanceRequestExtractor { String header(String key); + String query(String key); + String instanceId(); String serviceName(); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/Matcher.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/Matcher.java index 1cff86523b0..6fb18fada3d 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/Matcher.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/Matcher.java @@ -26,6 +26,8 @@ public class Matcher { private Map headers; + private Map queries; + private RawOperator apiPath; private List method; @@ -46,11 +48,18 @@ public Map getHeaders() { return headers; } - public void setHeaders( - Map headers) { + public void setHeaders(Map headers) { this.headers = headers; } + public Map getQueries() { + return queries; + } + + public void setQueries(Map queries) { + this.queries = queries; + } + public RawOperator getApiPath() { return apiPath; } diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/RequestProcessor.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/RequestProcessor.java index a17d3f72b40..9651d8a7740 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/RequestProcessor.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/RequestProcessor.java @@ -16,6 +16,7 @@ */ package org.apache.servicecomb.governance.marker; +import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -44,14 +45,13 @@ public class RequestProcessor implements ApplicationContextAware { public static final String infoMessageForCreatingClass = "is not in spring container, create one and register it to spring container"; - private static final String OPERATOR_SUFFIX = "Operator"; - private final Map operatorMap; private ApplicationContext applicationContext; public RequestProcessor(Map operatorMap) { - this.operatorMap = operatorMap; + this.operatorMap = new HashMap<>(operatorMap.size()); + operatorMap.forEach((k, v) -> this.operatorMap.put(v.name(), v)); } public boolean match(GovernanceRequestExtractor request, Matcher matcher) { @@ -64,6 +64,9 @@ public boolean match(GovernanceRequestExtractor request, Matcher matcher) { if (!headersMatch(request, matcher)) { return false; } + if (!queriesMatch(request, matcher)) { + return false; + } if (!serviceNameMatch(request, matcher)) { return false; } @@ -90,6 +93,19 @@ private boolean headersMatch(GovernanceRequestExtractor request, Matcher matcher return true; } + private boolean queriesMatch(GovernanceRequestExtractor request, Matcher matcher) { + if (matcher.getQueries() == null) { + return true; + } + for (Entry entry : matcher.getQueries().entrySet()) { + if (request.query(entry.getKey()) == null || + !operatorMatch(request.query(entry.getKey()), entry.getValue())) { + return false; + } + } + return true; + } + private boolean apiPathMatch(GovernanceRequestExtractor request, Matcher matcher) { if (matcher.getApiPath() == null) { return true; @@ -110,7 +126,7 @@ private boolean operatorMatch(String str, RawOperator rawOperator) { } for (Entry entry : rawOperator.entrySet()) { - MatchOperator operator = operatorMap.get(entry.getKey() + OPERATOR_SUFFIX); + MatchOperator operator = operatorMap.get(entry.getKey()); if (operator == null) { LOGGER.error("unsupported operator:" + entry.getKey() + ", please use one of :" + operatorMap.keySet()); return false; diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/CompareOperator.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/CompareOperator.java index ff491ecefe5..5b73b9aa939 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/CompareOperator.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/CompareOperator.java @@ -32,8 +32,13 @@ public CompareOperator() { charSet.add('!'); } + @Override + public String name() { + return "compare"; + } + /** - * 支持 > < = >= <= ! 后面加数字 + * 支持 {@code > < = >= <= !} 后面加数字 * * @param targetStr * @param patternStr diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ContainsOperator.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ContainsOperator.java index a5bb935f9b1..faa40521441 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ContainsOperator.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ContainsOperator.java @@ -18,6 +18,11 @@ public class ContainsOperator implements MatchOperator { + @Override + public String name() { + return "contains"; + } + @Override public boolean match(String targetStr, String patternStr) { return targetStr.contains(patternStr); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ExactOperator.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ExactOperator.java index 139d2f8cd99..cee8d48a27e 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ExactOperator.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ExactOperator.java @@ -20,6 +20,11 @@ public class ExactOperator implements MatchOperator { + @Override + public String name() { + return "exact"; + } + @Override public boolean match(String targetStr, String patternStr) { return StringUtils.equals(targetStr, patternStr); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/MatchOperator.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/MatchOperator.java index db92907455a..28abc1b8df5 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/MatchOperator.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/MatchOperator.java @@ -17,6 +17,7 @@ package org.apache.servicecomb.governance.marker.operator; public interface MatchOperator { + String name(); boolean match(String targetStr, String patternStr); } diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/PrefixOperator.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/PrefixOperator.java index e211d0d605f..6bb84c45a53 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/PrefixOperator.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/PrefixOperator.java @@ -20,6 +20,11 @@ import org.apache.commons.lang3.StringUtils; public class PrefixOperator implements MatchOperator { + @Override + public String name() { + return "prefix"; + } + @Override public boolean match(String requestValue, String patternValue) { return StringUtils.startsWith(requestValue, patternValue); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/SuffixOperator.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/SuffixOperator.java index 712b00b6bb0..99adf9fed1e 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/SuffixOperator.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/SuffixOperator.java @@ -20,6 +20,11 @@ import org.apache.commons.lang3.StringUtils; public class SuffixOperator implements MatchOperator { + @Override + public String name() { + return "suffix"; + } + @Override public boolean match(String requestValue, String patternValue) { return StringUtils.endsWith(requestValue, patternValue); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceProperties.java index f8bda95510e..05f42dfa5b1 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceProperties.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceProperties.java @@ -199,8 +199,8 @@ private boolean servicesMatch(String services) { return true; } - return Arrays.stream(services.split(",")).anyMatch(ser -> { - String[] serviceAndVersion = ser.split(":"); + return Arrays.stream(services.split(",")).anyMatch(service -> { + String[] serviceAndVersion = service.split(":"); if (serviceAndVersion.length == 1) { return microserviceMeta.getName().equals(serviceAndVersion[0]); } else if (serviceAndVersion.length == 2) { diff --git a/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java b/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java index 4b11c5481a3..9aa881ee8df 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java +++ b/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java @@ -16,6 +16,7 @@ */ package org.apache.servicecomb.router; +import org.apache.servicecomb.governance.marker.RequestProcessor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.match.RouterRuleMatcher; import org.springframework.context.annotation.Bean; @@ -25,17 +26,17 @@ @Configuration public class RouterCommonConfiguration { @Bean - public RouterRuleCache routerRuleCache(Environment environment) { + public RouterRuleCache scbRouterRuleCache(Environment environment) { return new RouterRuleCache(environment); } @Bean - public RouterRuleMatcher routerRuleMatcher(RouterRuleCache routerRuleCache) { - return new RouterRuleMatcher(routerRuleCache); + public RouterRuleMatcher scbRouterRuleMatcher(RouterRuleCache routerRuleCache, RequestProcessor requestProcessor) { + return new RouterRuleMatcher(routerRuleCache, requestProcessor); } @Bean - public RouterFilter routerFilter(RouterRuleMatcher routerRuleMatcher, RouterRuleCache routerRuleCache) { + public RouterFilter scbRouterFilter(RouterRuleMatcher routerRuleMatcher, RouterRuleCache routerRuleCache) { return new RouterFilter(routerRuleMatcher, routerRuleCache); } } diff --git a/governance/src/main/java/org/apache/servicecomb/router/RouterFilter.java b/governance/src/main/java/org/apache/servicecomb/router/RouterFilter.java index a6ec35d76f4..ab742e8613d 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/RouterFilter.java +++ b/governance/src/main/java/org/apache/servicecomb/router/RouterFilter.java @@ -17,9 +17,9 @@ package org.apache.servicecomb.router; import java.util.List; -import java.util.Map; import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.apache.servicecomb.router.match.RouterRuleMatcher; @@ -41,8 +41,8 @@ public RouterFilter(RouterRuleMatcher routerRuleMatcher, RouterRuleCache routerR this.routerRuleCache = routerRuleCache; } - public List getFilteredListOfServers(List list, - String targetServiceName, Map headers, RouterDistributor distributer) { + public List getFilteredListOfServers(List list, + String targetServiceName, GovernanceRequestExtractor extractor, RouterDistributor distributor) { if (CollectionUtils.isEmpty(list)) { return list; } @@ -55,7 +55,7 @@ public List getFilteredListOfServers(List list, return list; } // 2.match rule - PolicyRuleItem invokeRule = routerRuleMatcher.match(targetServiceName, headers); + PolicyRuleItem invokeRule = routerRuleMatcher.match(targetServiceName, extractor); if (invokeRule == null) { LOGGER.debug("route management match rule failed"); @@ -65,7 +65,7 @@ public List getFilteredListOfServers(List list, LOGGER.debug("route management match rule success: {}", invokeRule); // 3.distribute select endpoint - List resultList = distributer.distribute(targetServiceName, list, invokeRule); + List resultList = distributor.distribute(targetServiceName, list, invokeRule); LOGGER.debug("route management distribute rule success: {}", resultList); diff --git a/governance/src/main/java/org/apache/servicecomb/router/cache/RouterRuleCache.java b/governance/src/main/java/org/apache/servicecomb/router/cache/RouterRuleCache.java index 018d48ec593..7828c82d3a7 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/cache/RouterRuleCache.java +++ b/governance/src/main/java/org/apache/servicecomb/router/cache/RouterRuleCache.java @@ -46,6 +46,8 @@ public class RouterRuleCache { private static final String ROUTE_RULE = "servicecomb.routeRule.%s"; + public static final String GLOBAL_ROUTE_RULE_KEY = "servicecomb.globalRouteRule"; + private final Environment environment; private final ConcurrentHashMap serviceInfoCacheMap = new ConcurrentHashMap<>(); @@ -74,7 +76,7 @@ public boolean doInit(String targetServiceName) { if (serviceInfoCacheMap.containsKey(targetServiceName)) { return true; } - return addAllRule(targetServiceName, environment.getProperty(String.format(ROUTE_RULE, targetServiceName), "")); + return addAllRule(targetServiceName); } } return true; @@ -86,10 +88,17 @@ public void onConfigurationChangedEvent(GovernanceConfigurationChangedEvent even if (key.startsWith(ROUTE_RULE_PREFIX)) { serviceInfoCacheMap.remove(key.substring(ROUTE_RULE_PREFIX.length())); } + if (key.equals(GLOBAL_ROUTE_RULE_KEY)) { + serviceInfoCacheMap.clear(); + } } } - private boolean addAllRule(String targetServiceName, String ruleStr) { + private boolean addAllRule(String targetServiceName) { + String ruleStr = environment.getProperty(String.format(ROUTE_RULE, targetServiceName), ""); + if (StringUtils.isEmpty(ruleStr)) { + ruleStr = environment.getProperty(GLOBAL_ROUTE_RULE_KEY, ""); + } if (StringUtils.isEmpty(ruleStr)) { return false; } @@ -110,6 +119,8 @@ private boolean addAllRule(String targetServiceName, String ruleStr) { } ServiceInfoCache serviceInfoCache = new ServiceInfoCache(policyRuleItemList); serviceInfoCacheMap.put(targetServiceName, serviceInfoCache); + LOGGER.info("Route management serialization service {} rules success, content: {}", targetServiceName, + serviceInfoCache.getAllrule()); return true; } @@ -117,7 +128,8 @@ private boolean addAllRule(String targetServiceName, String ruleStr) { * if a server don't have rule , avoid registered too many callback , it may cause memory leak */ private boolean isServerContainRule(String targetServiceName) { - return !StringUtils.isEmpty(environment.getProperty(String.format(ROUTE_RULE, targetServiceName), "")); + return !StringUtils.isEmpty(environment.getProperty(String.format(ROUTE_RULE, targetServiceName), "")) || + !StringUtils.isEmpty(environment.getProperty(GLOBAL_ROUTE_RULE_KEY, "")); } public ConcurrentHashMap getServiceInfoCacheMap() { diff --git a/governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java b/governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java index d3f8f7640f5..d3905dfb133 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java +++ b/governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java @@ -17,34 +17,31 @@ package org.apache.servicecomb.router.distribute; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; -import java.util.stream.Collectors; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.model.PolicyRuleItem; import org.apache.servicecomb.router.model.RouteItem; import org.apache.servicecomb.router.model.TagItem; -import org.apache.servicecomb.router.util.VersionCompareUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.CollectionUtils; -public abstract class AbstractRouterDistributor implements - RouterDistributor { +public abstract class AbstractRouterDistributor implements + RouterDistributor { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRouterDistributor.class); - private Function getIns; + private Function getVersion; - private Function getVersion; + private Function getServerName; - private Function getServerName; - - private Function> getProperties; + private Function> getProperties; private RouterRuleCache routerRuleCache; @@ -56,115 +53,128 @@ public void setRouterRuleCache(RouterRuleCache routerRuleCache) { protected AbstractRouterDistributor() { } + /** + * distribute logic: + * 1、First according to the set route rules to choose target instances, if have just return. + * 2、if route rules not match instance, check if fallback rules are set, if set and match instances then return. + * 3、if route and fallback routes all have not match instance, then if route rules weight count less 100, return + * unset instances, otherwise return all instances. + * @param targetServiceName + * @param list + * @param invokeRule + * @return + */ @Override - public List distribute(String targetServiceName, List list, PolicyRuleItem invokeRule) { - //init LatestVersion - initLatestVersion(targetServiceName, list); + public List distribute(String targetServiceName, List list, PolicyRuleItem invokeRule) { + invokeRule.check(); - invokeRule.check( - routerRuleCache.getServiceInfoCacheMap().get(targetServiceName).getLatestVersionTag()); + // unSetTags instance list + List unSetTagInstances = new ArrayList<>(); - // get tag list - Map> versionServerMap = getDistributList(targetServiceName, list, invokeRule); + // record fallback router targItem instance + Map> fallbackVersionServerMap = new HashMap<>(); - if (CollectionUtils.isEmpty(versionServerMap)) { - LOGGER.debug("route management can not match any rule and route the latest version"); - return getLatestVersionList(list, targetServiceName); - } + // get tag instance map, fallbackVersionServerMap, unSetTagInstances + Map> versionServerMap = getDistributList(targetServiceName, list, invokeRule, + unSetTagInstances, fallbackVersionServerMap); - TagItem targetTag = getFiltedServerTagItem(invokeRule, targetServiceName); - if (versionServerMap.containsKey(targetTag)) { + // weight calculation to obtain the next tags instance + TagItem targetTag = getFilteredServerTagItem(invokeRule, targetServiceName); + if (targetTag != null && versionServerMap.containsKey(targetTag)) { return versionServerMap.get(targetTag); } - return getLatestVersionList(list, targetServiceName); + + if (!fallbackVersionServerMap.isEmpty()) { + // weight calculation to obtain the next fallback tags instance + TagItem fallbackTargetTag = getFallbackFilteredServerTagItem(invokeRule, targetServiceName); + if (fallbackTargetTag != null && fallbackVersionServerMap.containsKey(fallbackTargetTag)) { + return fallbackVersionServerMap.get(fallbackTargetTag); + } + } + + // has weightLess situation and unSetTagInstances has values + if (invokeRule.isWeightLess() && !unSetTagInstances.isEmpty()) { + return unSetTagInstances; + } + if (invokeRule.isEmptyProtection()) { + return list; + } + + // weight set 100 but not matched any instance, then return empty when emptyProtection close + return Collections.emptyList(); } @Override - public void init(Function getIns, - Function getVersion, - Function getServerName, - Function> getProperties) { - this.getIns = getIns; + public void init(Function getVersion, + Function getServerName, + Function> getProperties) { this.getVersion = getVersion; this.getServerName = getServerName; this.getProperties = getProperties; } - public TagItem getFiltedServerTagItem(PolicyRuleItem rule, String targetServiceName) { + public TagItem getFilteredServerTagItem(PolicyRuleItem rule, String targetServiceName) { return routerRuleCache.getServiceInfoCacheMap().get(targetServiceName) .getNextInvokeVersion(rule); } + public TagItem getFallbackFilteredServerTagItem(PolicyRuleItem rule, String targetServiceName) { + return routerRuleCache.getServiceInfoCacheMap().get(targetServiceName) + .getFallbackNextInvokeVersion(rule); + } + /** - * 1.filter targetService + * 1.filter set route rules targetService, build fallback targetService map and unSetTagInstances list. * 2.establish map is a more complicate way than direct traversal, because of multiple matches. * * the method getProperties() contains other field that we don't need. */ - private Map> getDistributList(String serviceName, - List list, - PolicyRuleItem invokeRule) { - String latestV = routerRuleCache.getServiceInfoCacheMap().get(serviceName).getLatestVersionTag() - .getVersion(); - Map> versionServerMap = new HashMap<>(); - for (T server : list) { + private Map> getDistributList(String serviceName, List list, + PolicyRuleItem invokeRule, List unSetTagInstances, Map> fallbackVersionMap) { + Map> versionServerMap = new HashMap<>(); + for (INSTANCE instance : list) { //get server - E ms = getIns.apply(server); - if (getServerName.apply(ms).equals(serviceName)) { - //most matching - TagItem tagItem = new TagItem(getVersion.apply(ms), getProperties.apply(ms)); - TagItem targetTag = null; - int maxMatch = 0; - for (RouteItem entry : invokeRule.getRoute()) { - if (entry.getTagitem() == null){ - continue; - } - int nowMatch = entry.getTagitem().matchNum(tagItem); - if (nowMatch > maxMatch) { - maxMatch = nowMatch; - targetTag = entry.getTagitem(); - } - } - if (invokeRule.isWeightLess() && getVersion.apply(ms).equals(latestV)) { - TagItem latestVTag = invokeRule.getRoute().get(invokeRule.getRoute().size() - 1) - .getTagitem(); - if (!versionServerMap.containsKey(latestVTag)) { - versionServerMap.put(latestVTag, new ArrayList<>()); - } - versionServerMap.get(latestVTag).add(server); - } + if (getServerName.apply(instance).equals(serviceName)) { + TagItem tagItem = new TagItem(getVersion.apply(instance), getProperties.apply(instance)); + // route most matching TagItem + TagItem targetTag = buildTargetTag(invokeRule.getRoute(), tagItem); if (targetTag != null) { if (!versionServerMap.containsKey(targetTag)) { versionServerMap.put(targetTag, new ArrayList<>()); } - versionServerMap.get(targetTag).add(server); + versionServerMap.get(targetTag).add(instance); + } else { + // not matched, placed in the unset tag instances collection + unSetTagInstances.add(instance); + } + // ensure the tags can build when set for both route and fallback at the same time + if (!CollectionUtils.isEmpty(invokeRule.getFallback())) { + // fallback most matching TagItem + TagItem targetTagFallback = buildTargetTag(invokeRule.getFallback(), tagItem); + if (!fallbackVersionMap.containsKey(targetTagFallback)) { + fallbackVersionMap.put(targetTagFallback, new ArrayList<>()); + } + fallbackVersionMap.get(targetTagFallback).add(instance); } } } return versionServerMap; } - - public void initLatestVersion(String serviceName, List list) { - String latestVersion = null; - for (T server : list) { - E ms = getIns.apply(server); - if (getServerName.apply(ms).equals(serviceName)) { - if (latestVersion == null || VersionCompareUtil - .compareVersion(latestVersion, getVersion.apply(ms)) == -1) { - latestVersion = getVersion.apply(ms); - } + private TagItem buildTargetTag(List route, TagItem tagItem) { + int maxMatch = 0; + TagItem targetTag = null; + // obtain the rule with the most parameter matches + for (RouteItem entry : route) { + if (entry.getTagitem() == null){ + continue; + } + int nowMatch = entry.getTagitem().matchNum(tagItem); + if (nowMatch > maxMatch) { + maxMatch = nowMatch; + targetTag = entry.getTagitem(); } } - TagItem tagitem = new TagItem(latestVersion); - routerRuleCache.getServiceInfoCacheMap().get(serviceName).setLatestVersionTag(tagitem); - } - - public List getLatestVersionList(List list, String targetServiceName) { - String latestV = routerRuleCache.getServiceInfoCacheMap().get(targetServiceName) - .getLatestVersionTag().getVersion(); - return list.stream().filter(server -> - getVersion.apply(getIns.apply(server)).equals(latestV) - ).collect(Collectors.toList()); + return targetTag; } } diff --git a/governance/src/main/java/org/apache/servicecomb/router/distribute/RouterDistributor.java b/governance/src/main/java/org/apache/servicecomb/router/distribute/RouterDistributor.java index fe5cf37b14f..5f5babdec34 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/distribute/RouterDistributor.java +++ b/governance/src/main/java/org/apache/servicecomb/router/distribute/RouterDistributor.java @@ -19,17 +19,18 @@ import java.util.List; import java.util.Map; import java.util.function.Function; + import org.apache.servicecomb.router.model.PolicyRuleItem; /** - * @Author GuoYl123 - * @Date 2019/10/17 + * @author GuoYl123 + * @since 2019/10/17 **/ -public interface RouterDistributor { +public interface RouterDistributor { - void init(Function getIns, Function getVersion, - Function getServerName, - Function> getProperties); + void init(Function getVersion, + Function getServerName, + Function> getProperties); - List distribute(String targetServiceName, List list, PolicyRuleItem invokeRule); + List distribute(String targetServiceName, List list, PolicyRuleItem invokeRule); } diff --git a/governance/src/main/java/org/apache/servicecomb/router/exception/RouterIllegalParamException.java b/governance/src/main/java/org/apache/servicecomb/router/exception/RouterIllegalParamException.java index 61fda32c8e4..9647a853ea8 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/exception/RouterIllegalParamException.java +++ b/governance/src/main/java/org/apache/servicecomb/router/exception/RouterIllegalParamException.java @@ -17,8 +17,8 @@ package org.apache.servicecomb.router.exception; /** - * @Author GuoYl123 - * @Date 2019/11/4 + * @author GuoYl123 + * @since 2019/11/4 **/ public class RouterIllegalParamException extends RuntimeException { diff --git a/governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java b/governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java index a7fcb0f2811..dd2ba78d7d3 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java +++ b/governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java @@ -16,22 +16,25 @@ */ package org.apache.servicecomb.router.match; -import java.util.Map; - +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.marker.RequestProcessor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.model.PolicyRuleItem; public class RouterRuleMatcher { private final RouterRuleCache routerRuleCache; - public RouterRuleMatcher(RouterRuleCache routerRuleCache) { + private final RequestProcessor requestProcessor; + + public RouterRuleMatcher(RouterRuleCache routerRuleCache, RequestProcessor requestProcessor) { this.routerRuleCache = routerRuleCache; + this.requestProcessor = requestProcessor; } - public PolicyRuleItem match(String serviceName, Map invokeHeader) { + public PolicyRuleItem match(String serviceName, GovernanceRequestExtractor request) { for (PolicyRuleItem rule : routerRuleCache.getServiceInfoCacheMap().get(serviceName) .getAllrule()) { - if (rule.getMatch() == null || rule.getMatch().match(invokeHeader)) { + if (rule.getMatch() == null || requestProcessor.match(request, rule.getMatch())) { return rule; } } diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/HeaderRule.java b/governance/src/main/java/org/apache/servicecomb/router/model/HeaderRule.java index 931fcee2273..7a8afeda77f 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/model/HeaderRule.java +++ b/governance/src/main/java/org/apache/servicecomb/router/model/HeaderRule.java @@ -21,8 +21,8 @@ import org.slf4j.LoggerFactory; /** - * @Author GuoYl123 - * @Date 2019/10/17 + * @author GuoYl123 + * @since 2019/10/17 **/ public class HeaderRule { diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/Matcher.java b/governance/src/main/java/org/apache/servicecomb/router/model/Matcher.java deleted file mode 100644 index 7db815dd267..00000000000 --- a/governance/src/main/java/org/apache/servicecomb/router/model/Matcher.java +++ /dev/null @@ -1,93 +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.servicecomb.router.model; - -import java.util.Map; -import org.springframework.util.CollectionUtils; - -/** - * @Author GuoYl123 - * @Date 2019/10/17 - **/ -public class Matcher { - - private String source; - - private Map sourceTags; - - private Map headers; - - private String refer; - - public Matcher() { - } - - public boolean match(Map realHeaders) { - if (CollectionUtils.isEmpty(headers)) { - return true; - } - for (Map.Entry entry : headers.entrySet()) { - if (!realHeaders.containsKey(entry.getKey()) || !entry.getValue() - .match(realHeaders.get(entry.getKey()))) { - return false; - } - } - return true; - } - - public String getSource() { - return source; - } - - public void setSource(String source) { - this.source = source; - } - - public Map getSourceTags() { - return sourceTags; - } - - public void setSourceTags(Map sourceTags) { - this.sourceTags = sourceTags; - } - - public Map getHeaders() { - return headers; - } - - public void setHeaders(Map headers) { - this.headers = headers; - } - - public String getRefer() { - return refer; - } - - public void setRefer(String refer) { - this.refer = refer; - } - - @Override - public String toString() { - return "Matcher{" + - "source='" + source + '\'' + - ", sourceTags=" + sourceTags + - ", headers=" + headers + - ", refer='" + refer + '\'' + - '}'; - } -} diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java b/governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java index edf950505e5..0700a2618d8 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java +++ b/governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java @@ -18,14 +18,15 @@ import java.util.List; +import org.apache.servicecomb.governance.marker.Matcher; import org.apache.servicecomb.router.exception.RouterIllegalParamException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; /** - * @Author GuoYl123 - * @Date 2019/10/17 + * @author GuoYl123 + * @since 2019/10/17 **/ public class PolicyRuleItem implements Comparable { @@ -35,29 +36,29 @@ public class PolicyRuleItem implements Comparable { private Matcher match; - // any match private List route; private Integer total; private boolean weightLess = false; + private List fallback; + + private Integer fallbackTotal; + + private boolean emptyProtection = true; + public PolicyRuleItem() { } /** * if weight is less than 100, fill with minimum version * - * @param latestVersionTag */ - public void check(TagItem latestVersionTag) { + public void check() { if (CollectionUtils.isEmpty(route)) { throw new RouterIllegalParamException("canary rule list can not be null"); } - if (route.size() == 1) { - route.get(0).setWeight(100); - return; - } int sum = 0; for (RouteItem item : route) { if (item.getWeight() == null) { @@ -68,11 +69,8 @@ public void check(TagItem latestVersionTag) { if (sum > 100) { LOGGER.warn("canary rule weight sum is more than 100"); } else if (sum < 100) { - if (latestVersionTag == null) { - LOGGER.warn("canary has some error when set default latestVersion"); - } weightLess = true; - route.add(new RouteItem(100 - sum, latestVersionTag)); + route.add(new RouteItem(100 - sum, null)); } } @@ -121,6 +119,30 @@ public void setWeightLess(boolean weightLess) { this.weightLess = weightLess; } + public List getFallback() { + return fallback; + } + + public void setFallback(List fallback) { + this.fallback = fallback; + } + + public Integer getFallbackTotal() { + return fallbackTotal; + } + + public void setFallbackTotal(Integer fallbackTotal) { + this.fallbackTotal = fallbackTotal; + } + + public boolean isEmptyProtection() { + return emptyProtection; + } + + public void setEmptyProtection(boolean emptyProtection) { + this.emptyProtection = emptyProtection; + } + @Override public String toString() { return "PolicyRuleItem{" + @@ -129,6 +151,9 @@ public String toString() { ", route=" + route + ", total=" + total + ", weightLess=" + weightLess + + ", fallback=" + fallback + + ", fallbackTotal=" + fallbackTotal + + ", emptyProtection=" + emptyProtection + '}'; } } diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java b/governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java index f2017a923e0..5f1d6e9f8df 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java +++ b/governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java @@ -19,8 +19,8 @@ import java.util.Map; /** - * @Author GuoYl123 - * @Date 2019/10/17 + * @author GuoYl123 + * @since 2019/10/17 **/ public class RouteItem implements Comparable { diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/ServiceInfoCache.java b/governance/src/main/java/org/apache/servicecomb/router/model/ServiceInfoCache.java index 15cc08c949b..232f1da6338 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/model/ServiceInfoCache.java +++ b/governance/src/main/java/org/apache/servicecomb/router/model/ServiceInfoCache.java @@ -19,9 +19,11 @@ import java.util.List; import java.util.stream.Collectors; +import org.springframework.util.CollectionUtils; + /** - * @Author GuoYl123 - * @Date 2019/10/17 + * @author GuoYl123 + * @since 2019/10/17 **/ public class ServiceInfoCache { private final List allrule; @@ -34,9 +36,12 @@ public class ServiceInfoCache { public ServiceInfoCache(List policyRuleItemList) { this.allrule = policyRuleItemList.stream().sorted().collect(Collectors.toList()); - this.getAllrule().forEach(rule -> - rule.getRoute().forEach(RouteItem::initTagItem) - ); + this.getAllrule().forEach(rule -> { + rule.getRoute().forEach(RouteItem::initTagItem); + if (!CollectionUtils.isEmpty(rule.getFallback())) { + rule.getFallback().forEach(RouteItem::initTagItem); + } + }); } public TagItem getNextInvokeVersion(PolicyRuleItem policyRuleItem) { @@ -44,7 +49,19 @@ public TagItem getNextInvokeVersion(PolicyRuleItem policyRuleItem) { if (policyRuleItem.getTotal() == null) { policyRuleItem.setTotal(rule.stream().mapToInt(RouteItem::getWeight).sum()); } - rule.stream().forEach(RouteItem::addCurrentWeight); + return calculateWeight(rule, policyRuleItem.getTotal()); + } + + public TagItem getFallbackNextInvokeVersion(PolicyRuleItem policyRuleItem) { + List rule = policyRuleItem.getFallback(); + if (policyRuleItem.getFallbackTotal() == null) { + policyRuleItem.setFallbackTotal(rule.stream().mapToInt(RouteItem::getWeight).sum()); + } + return calculateWeight(rule, policyRuleItem.getFallbackTotal()); + } + + private TagItem calculateWeight(List rule, int total) { + rule.forEach(RouteItem::addCurrentWeight); int maxIndex = 0, maxWeight = -1; for (int i = 0; i < rule.size(); i++) { if (maxWeight < rule.get(i).getCurrentWeight()) { @@ -52,7 +69,7 @@ public TagItem getNextInvokeVersion(PolicyRuleItem policyRuleItem) { maxWeight = rule.get(i).getCurrentWeight(); } } - rule.get(maxIndex).reduceCurrentWeight(policyRuleItem.getTotal()); + rule.get(maxIndex).reduceCurrentWeight(total); return rule.get(maxIndex).getTagitem(); } diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/TagItem.java b/governance/src/main/java/org/apache/servicecomb/router/model/TagItem.java index 0fc610df769..a7a20fab7ea 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/model/TagItem.java +++ b/governance/src/main/java/org/apache/servicecomb/router/model/TagItem.java @@ -21,8 +21,8 @@ import java.util.Objects; /** - * @Author GuoYl123 - * @Date 2019/10/17 + * @author GuoYl123 + * @since 2019/10/17 **/ public class TagItem { diff --git a/governance/src/main/resources/META-INF/spring.factories b/governance/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 2a3f4f6e7cb..00000000000 --- a/governance/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,19 +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. -# -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.governance.GovernanceCommonConfiguration,\ - org.apache.servicecomb.router.RouterCommonConfiguration diff --git a/governance/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/governance/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..a67e4ed8d0a --- /dev/null +++ b/governance/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,19 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.governance.GovernanceCommonConfiguration +org.apache.servicecomb.router.RouterCommonConfiguration diff --git a/governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java b/governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java index 9c84287ac5c..dedeba80053 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java @@ -35,8 +35,8 @@ protected String extractStatusCode(Object result) { } @Override - public boolean isFailedResult(Throwable e) { - return super.isFailedResult(e); + public boolean isFailedResult(List statusList, Throwable e) { + return super.isFailedResult(statusList, e); } } diff --git a/governance/src/test/java/org/apache/servicecomb/governance/BulkheadHandlerTest.java b/governance/src/test/java/org/apache/servicecomb/governance/BulkheadHandlerTest.java index 4e77fa900b3..59703d4ee7a 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/BulkheadHandlerTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/BulkheadHandlerTest.java @@ -32,8 +32,8 @@ public class BulkheadHandlerTest { private BulkheadHandler bulkheadHandler; @Autowired - public void setInstanceIsolationHandler(BulkheadHandler bulkheadHandler) { - this.bulkheadHandler = bulkheadHandler; + public void setInstanceIsolationHandler(BulkheadHandler scbBulkheadHandler) { + this.bulkheadHandler = scbBulkheadHandler; } @Test diff --git a/governance/src/test/java/org/apache/servicecomb/governance/CustomMatchTest.java b/governance/src/test/java/org/apache/servicecomb/governance/CustomMatchTest.java index 92890baa663..a0e0c2d8ab9 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/CustomMatchTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/CustomMatchTest.java @@ -90,7 +90,7 @@ public void test_should_pass_when_multiple_value() { public void test_should_throw_exception_when_not_implements_interface() { GovernanceRequest request = new GovernanceRequest(); try { - Matcher mockMatcher = generateMatcher("classNotImplments", "bill,bill2"); + Matcher mockMatcher = generateMatcher("classNotImplements", "bill,bill2"); this.requestProcessor.match(request, mockMatcher); Assertions.fail("an exception is expected!"); } catch (Exception e) { diff --git a/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java b/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java index 919f260d0b1..7733b8ed44f 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java @@ -42,9 +42,9 @@ public class FaultInjectionTest { private FaultInjectionHandler faultInjectionHandler2; @Autowired - public void setFaultInjectionHandler(FaultInjectionHandler faultInjectionHandler, + public void setFaultInjectionHandler(FaultInjectionHandler scbFaultInjectionHandler, @Qualifier("faultInjectionHandler2") FaultInjectionHandler faultInjectionHandler2) { - this.faultInjectionHandler = faultInjectionHandler; + this.faultInjectionHandler = scbFaultInjectionHandler; this.faultInjectionHandler2 = faultInjectionHandler2; } diff --git a/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java b/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java index 4f12dec62f2..5b71a2ac3b8 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java @@ -89,49 +89,49 @@ public void setPropertiesList(List queries = new HashMap<>(); + queries.put("name", "bob"); + request.setQueries(queries); + Mapper mapper = mapperHandler.getActuator(request); + Assertions.assertEquals(1, mapper.target().size()); + Assertions.assertEquals("$Q{name}", mapper.target().get("user-id")); + } + @Test public void test_mapper2_work() { GovernanceRequest request = new GovernanceRequest(); diff --git a/governance/src/test/java/org/apache/servicecomb/governance/MockConfiguration.java b/governance/src/test/java/org/apache/servicecomb/governance/MockConfiguration.java index b75d9cdb942..85c04f39590 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/MockConfiguration.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/MockConfiguration.java @@ -30,8 +30,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import io.micrometer.prometheus.PrometheusConfig; -import io.micrometer.prometheus.PrometheusMeterRegistry; +import io.micrometer.prometheusmetrics.PrometheusConfig; +import io.micrometer.prometheusmetrics.PrometheusMeterRegistry; + @Configuration public class MockConfiguration { diff --git a/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java b/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java index 1dd272ca187..2badc9c1548 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java @@ -34,7 +34,7 @@ protected String extractStatusCode(Object result) { } @Override - public boolean isFailedResult(Throwable e) { + public boolean isFailedResult(List statusList, Throwable e) { return false; } } diff --git a/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java b/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java index 1223bfe7465..073cfc44534 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java @@ -35,7 +35,7 @@ public void testNotFailAfterMaxAttemptsWhenThrow() { AbstractRetryExtension retryExtension = Mockito.mock(AbstractRetryExtension.class); RetryProperties retryProperties = Mockito.mock(RetryProperties.class); GovernanceRequest governanceRequest = Mockito.mock(GovernanceRequest.class); - Mockito.when(retryExtension.isFailedResult(Mockito.any())).thenReturn(true); + Mockito.when(retryExtension.isFailedResult(Mockito.any(), Mockito.any())).thenReturn(true); RetryPolicy retryPolicy = new RetryPolicy(); retryPolicy.setName("test"); @@ -53,7 +53,7 @@ public void testFailAfterMaxAttemptsWhenThrow() { AbstractRetryExtension retryExtension = Mockito.mock(AbstractRetryExtension.class); RetryProperties retryProperties = Mockito.mock(RetryProperties.class); GovernanceRequest governanceRequest = Mockito.mock(GovernanceRequest.class); - Mockito.when(retryExtension.isFailedResult(Mockito.any())).thenReturn(true); + Mockito.when(retryExtension.isFailedResult(Mockito.any(), Mockito.any())).thenReturn(true); RetryPolicy retryPolicy = new RetryPolicy(); retryPolicy.setName("test"); @@ -71,7 +71,7 @@ public void testFailAfterMaxAttemptsOnResult() { AbstractRetryExtension retryExtension = Mockito.mock(AbstractRetryExtension.class); RetryProperties retryProperties = Mockito.mock(RetryProperties.class); GovernanceRequest governanceRequest = Mockito.mock(GovernanceRequest.class); - Mockito.when(retryExtension.isFailedResult(Mockito.any(), Mockito.any())).thenReturn(true); + Mockito.when(retryExtension.isFailedResult(Mockito.any(), (Object) Mockito.any())).thenReturn(true); RetryPolicy retryPolicy = new RetryPolicy(); retryPolicy.setName("test"); diff --git a/governance/src/test/java/org/apache/servicecomb/governance/handler/ext/RetryExtensionTest.java b/governance/src/test/java/org/apache/servicecomb/governance/handler/ext/RetryExtensionTest.java index 15a0c5489d9..41157a9b079 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/handler/ext/RetryExtensionTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/handler/ext/RetryExtensionTest.java @@ -24,16 +24,16 @@ import java.util.Arrays; import java.util.List; -import org.junit.Test; + import org.junit.jupiter.api.Assertions; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; import io.vertx.core.VertxException; -@RunWith(SpringRunner.class) +@SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RetryExtensionTest { @@ -134,7 +134,7 @@ public void testRetryNoRouteToHostException() { @Test public void testRetryEqualTen() { - Exception target = new ConnectException("connectin refused"); + Exception target = new ConnectException("connection refused"); for (int i = 0; i < 8; i++) { target = new Exception("Level" + i, target); } @@ -145,7 +145,7 @@ public void testRetryEqualTen() { @Test public void testRetryOverTen() { - Exception target = new ConnectException("connectin refused"); + Exception target = new ConnectException("connection refused"); for (int i = 0; i < 9; i++) { target = new Exception("Level" + i, target); } diff --git a/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassNotImplements.java b/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassNotImplements.java new file mode 100644 index 00000000000..51336e60ed5 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassNotImplements.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.governance.mockclasses; + +import org.springframework.stereotype.Component; +import org.apache.servicecomb.governance.marker.GovernanceRequest; + +@Component +public class ClassNotImplements { + public boolean matchRequest(GovernanceRequest request, String parameters) { + return true; + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassNotImplments.java b/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassNotImplments.java deleted file mode 100644 index 85d14fe0834..00000000000 --- a/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassNotImplments.java +++ /dev/null @@ -1,28 +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.servicecomb.governance.mockclasses; - -import org.springframework.stereotype.Component; -import org.apache.servicecomb.governance.marker.GovernanceRequest; - -@Component -public class ClassNotImplments { - public boolean matchRequest(GovernanceRequest request, String parameters) { - return true; - } -} diff --git a/governance/src/test/java/org/apache/servicecomb/router/ExampleDistributor.java b/governance/src/test/java/org/apache/servicecomb/router/ExampleDistributor.java index 7c6aa04f945..42fdfad0367 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/ExampleDistributor.java +++ b/governance/src/test/java/org/apache/servicecomb/router/ExampleDistributor.java @@ -21,8 +21,8 @@ import org.springframework.stereotype.Component; @Component -public class ExampleDistributor extends AbstractRouterDistributor { +public class ExampleDistributor extends AbstractRouterDistributor { public ExampleDistributor() { - init(a -> a, ServiceIns::getVersion, ServiceIns::getServerName, ServiceIns::getTags); + init(ServiceIns::getVersion, ServiceIns::getServerName, ServiceIns::getTags); } } diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java index f3f36e45399..cc8cc9ce96c 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java @@ -26,21 +26,23 @@ import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; -import org.junit.Before; -import org.junit.Test; + import org.junit.jupiter.api.Assertions; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; -@RunWith(SpringRunner.class) +@SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorDynamicConfig2Test { private static final String TARGET_SERVICE_NAME = "test_server"; @@ -70,7 +72,7 @@ public class RouterDistributorDynamicConfig2Test { private RouterFilter routerFilter; - private RouterDistributor testDistributor; + private RouterDistributor testDistributor; @Autowired public void setEnvironment(Environment environment) { @@ -83,13 +85,13 @@ public void setRouterFilter(RouterFilter routerFilter) { } @Autowired - public void setTestDistributor(RouterDistributor testDistributor) { + public void setTestDistributor(RouterDistributor testDistributor) { this.testDistributor = testDistributor; } private final Map dynamicValues = new HashMap<>(); - @Before + @BeforeEach public void setUp() { ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; @@ -124,9 +126,11 @@ private void postConfigurationChangedEvent() { @Test public void testMatchPrecedenceHigher() { + GovernanceRequest governanceRequest = new GovernanceRequest(); Map headers = new HashMap<>(); headers.put("userId", "01"); headers.put("appId", "01"); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -136,7 +140,7 @@ public void testMatchPrecedenceHigher() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("01", resultServerList.get(0).getId()); } @@ -164,6 +168,8 @@ public void testCaseSensitiveNotMatch() { Map headers = new HashMap<>(); headers.put("userId", "User01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -173,7 +179,7 @@ public void testCaseSensitiveNotMatch() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("01", resultServerList.get(0).getId()); Assertions.assertEquals("2.0", resultServerList.get(0).getVersion()); @@ -207,6 +213,8 @@ public void testNoneMatch() { Map headers = new HashMap<>(); headers.put("userId", "User01"); headers.put("appId", "App01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -216,7 +224,7 @@ public void testNoneMatch() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(2, resultServerList.size()); } @@ -248,6 +256,8 @@ public void testOneMatchButNoInstance() { Map headers = new HashMap<>(); headers.put("userId", "user01"); headers.put("appId", "app02"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -257,13 +267,13 @@ public void testOneMatchButNoInstance() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(2, resultServerList.size()); } - private List mainFilter(List serverList, Map headers) { + private List mainFilter(List serverList, GovernanceRequestExtractor requestExtractor) { return routerFilter - .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, headers, + .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, requestExtractor, testDistributor); } } diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java index f57ed65794b..4418a30829b 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java @@ -18,7 +18,6 @@ package org.apache.servicecomb.router; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -27,21 +26,22 @@ import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; -import org.junit.Before; -import org.junit.Test; import org.junit.jupiter.api.Assertions; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; -@RunWith(SpringRunner.class) +@SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorDynamicConfigTest { private static final String TARGET_SERVICE_NAME = "test_server"; @@ -51,10 +51,9 @@ public class RouterDistributorDynamicConfigTest { + " match: #匹配策略\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 01\n" + + " exact: \"01\"\n" + " route: #路由规则\n" + " - weight: 50\n" + " tags:\n" @@ -63,10 +62,9 @@ public class RouterDistributorDynamicConfigTest { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 02\n" + + " exact: \"02\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -75,10 +73,9 @@ public class RouterDistributorDynamicConfigTest { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 03\n" + + " exact: \"03\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -88,7 +85,7 @@ public class RouterDistributorDynamicConfigTest { private RouterFilter routerFilter; - private RouterDistributor testDistributor; + private RouterDistributor testDistributor; @Autowired public void setEnvironment(Environment environment) { @@ -101,7 +98,7 @@ public void setRouterFilter(RouterFilter routerFilter) { } @Autowired - public void setTestDistributor(RouterDistributor testDistributor) { + public void setTestDistributor(RouterDistributor testDistributor) { this.testDistributor = testDistributor; } @@ -110,7 +107,7 @@ public RouterDistributorDynamicConfigTest() { private final Map dynamicValues = new HashMap<>(); - @Before + @BeforeEach public void setUp() { ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; @@ -143,7 +140,7 @@ public String[] getPropertyNames() { @Test public void testHeaderIsEmpty() { List list = getMockList(); - List serverList = mainFilter(list, Collections.emptyMap()); + List serverList = mainFilter(list, new GovernanceRequest()); Assertions.assertEquals(2, serverList.size()); } @@ -152,9 +149,11 @@ public void testVersionNotMatch() { Map headerMap = new HashMap<>(); headerMap.put("userId", "02"); headerMap.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headerMap); List list = getMockList(); list.remove(1); - List serverList = mainFilter(list, headerMap); + List serverList = mainFilter(list, governanceRequest); Assertions.assertEquals(1, serverList.size()); Assertions.assertEquals("01", serverList.get(0).getId()); } @@ -164,7 +163,9 @@ public void testVersionMatch() { Map headers = new HashMap<>(); headers.put("userId", "01"); headers.put("appId", "01"); - List serverList = mainFilter(getMockList(), headers); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); + List serverList = mainFilter(getMockList(), governanceRequest); Assertions.assertEquals(1, serverList.size()); Assertions.assertEquals("02", serverList.get(0).getId()); } @@ -174,7 +175,9 @@ public void testMatchPrecedenceLower() { Map headers = new HashMap<>(); headers.put("userId", "02"); headers.put("appId", "01"); - List serverList = mainFilter(getMockList(), headers); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); + List serverList = mainFilter(getMockList(), governanceRequest); Assertions.assertEquals(1, serverList.size()); Assertions.assertEquals("01", serverList.get(0).getId()); } @@ -184,6 +187,8 @@ public void testMatchPrecedenceHigher() { Map headers = new HashMap<>(); headers.put("userId", "03"); headers.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -194,7 +199,7 @@ public void testMatchPrecedenceHigher() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("02", resultServerList.get(0).getId()); } @@ -210,9 +215,10 @@ private List getMockList() { return serverList; } - private List mainFilter(List serverList, Map headers) { + private List mainFilter(List serverList, + GovernanceRequestExtractor governanceRequestExtractor) { return routerFilter - .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, headers, + .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, governanceRequestExtractor, testDistributor); } } diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java index 9e5be69376f..54ebcb05823 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java @@ -16,27 +16,27 @@ */ package org.apache.servicecomb.router; +import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.router.distribute.RouterDistributor; -import org.junit.Test; import org.junit.jupiter.api.Assertions; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -@RunWith(SpringRunner.class) +@SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorFileConfigTest { private static final String TARGET_SERVICE_NAME = "test_server1"; private RouterFilter routerFilter; - private RouterDistributor routerDistributor; + private RouterDistributor routerDistributor; @Autowired public void setRouterFilter(RouterFilter routerFilter) { @@ -44,7 +44,7 @@ public void setRouterFilter(RouterFilter routerFilter) { } @Autowired - public void setRouterDistributor(RouterDistributor routerDistributor) { + public void setRouterDistributor(RouterDistributor routerDistributor) { this.routerDistributor = routerDistributor; } @@ -52,8 +52,11 @@ public void setRouterDistributor(RouterDistributor route public void testDistribute() { List list = initServiceList(); HashMap header = new HashMap<>(); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(header); + List listOfServers = routerFilter - .getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor); + .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); Assertions.assertNotNull(listOfServers); for (ServiceIns server : listOfServers) { Assertions.assertEquals(TARGET_SERVICE_NAME, server.getServerName()); @@ -63,7 +66,7 @@ public void testDistribute() { for (int i = 0; i < 10; i++) { List serverList = routerFilter - .getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor); + .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); for (ServiceIns serviceIns : serverList) { if ("01".equals(serviceIns.getId())) { serverNum1++; diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWeightLessTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWeightLessTest.java new file mode 100644 index 00000000000..68b04bc9cdb --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWeightLessTest.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.servicecomb.router; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.router.distribute.RouterDistributor; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) +public class RouterDistributorFileWeightLessTest { + private static final String TARGET_SERVICE_NAME = "test_server2"; + + private RouterFilter routerFilter; + + private RouterDistributor routerDistributor; + + @Autowired + public void setRouterFilter(RouterFilter routerFilter) { + this.routerFilter = routerFilter; + } + + @Autowired + public void setRouterDistributor(RouterDistributor routerDistributor) { + this.routerDistributor = routerDistributor; + } + + @Test + public void testDistribute() { + List list = initServiceList(); + HashMap header = new HashMap<>(); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(header); + List listOfServers = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); + Assertions.assertNotNull(listOfServers); + for (ServiceIns server : listOfServers) { + Assertions.assertEquals(TARGET_SERVICE_NAME, server.getServerName()); + } + int serverNum1 = 0; + int unSetTagNum = 0; + + for (int i = 0; i < 10; i++) { + List serverList = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); + for (ServiceIns serviceIns : serverList) { + if ("01".equals(serviceIns.getId())) { + serverNum1++; + } else if ("02".equals(serviceIns.getId())) { + unSetTagNum++; + } + } + } + boolean flag = false; + if (Math.round(unSetTagNum * 1.0 / serverNum1) == 4) { + flag = true; + } + Assertions.assertTrue(flag); + } + + List initServiceList() { + ServiceIns serviceIns1 = new ServiceIns("01", "test_server2"); + ServiceIns serviceIns2 = new ServiceIns("02", "test_server2"); + serviceIns1.setVersion("1.0"); + serviceIns2.setVersion("2.0"); + serviceIns1.addTags("x-group", "red"); + serviceIns2.addTags("x-group", "green"); + List list = new ArrayList<>(); + list.add(serviceIns1); + list.add(serviceIns2); + return list; + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWithFallbackTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWithFallbackTest.java new file mode 100644 index 00000000000..2d7a1064557 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWithFallbackTest.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.servicecomb.router; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.router.distribute.RouterDistributor; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) +public class RouterDistributorFileWithFallbackTest { + private static final String TARGET_SERVICE_NAME_WITHOUT_FALLBACK = "test_server3"; + + private static final String TARGET_SERVICE_NAME_WITH_FALLBACK = "test_server4"; + + private static final String TARGET_SERVICE_NAME_ROUTE_FALLBACK = "test_server5"; + + private RouterFilter routerFilter; + + private RouterDistributor routerDistributor; + + @Autowired + public void setRouterFilter(RouterFilter routerFilter) { + this.routerFilter = routerFilter; + } + + @Autowired + public void setRouterDistributor(RouterDistributor routerDistributor) { + this.routerDistributor = routerDistributor; + } + + @Test + public void testDistributeWithoutFallback() { + List list = initServiceList(TARGET_SERVICE_NAME_WITHOUT_FALLBACK); + HashMap header = new HashMap<>(); + header.put("canary", "canary"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(header); + List listOfServers = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME_WITHOUT_FALLBACK, governanceRequest, routerDistributor); + Assertions.assertNotNull(listOfServers); + for (ServiceIns server : listOfServers) { + Assertions.assertEquals(TARGET_SERVICE_NAME_WITHOUT_FALLBACK, server.getServerName()); + } + int serverNum1 = 0; + int serverNum2 = 0; + + for (int i = 0; i < 10; i++) { + List serverList = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME_WITHOUT_FALLBACK, governanceRequest, routerDistributor); + for (ServiceIns serviceIns : serverList) { + if ("01".equals(serviceIns.getId())) { + serverNum1++; + } else if ("02".equals(serviceIns.getId())) { + serverNum2++; + } + } + } + Assertions.assertTrue(serverNum2 == serverNum1); + } + + @Test + public void testDistributeWithFallback() { + List list = initServiceList(TARGET_SERVICE_NAME_WITH_FALLBACK); + HashMap header = new HashMap<>(); + header.put("canary", "canary"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(header); + List listOfServers = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME_WITH_FALLBACK, governanceRequest, routerDistributor); + Assertions.assertNotNull(listOfServers); + for (ServiceIns server : listOfServers) { + Assertions.assertEquals(TARGET_SERVICE_NAME_WITH_FALLBACK, server.getServerName()); + } + int serverNum1 = 0; + int serverNum2 = 0; + + for (int i = 0; i < 10; i++) { + List serverList = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME_WITH_FALLBACK, governanceRequest, routerDistributor); + for (ServiceIns serviceIns : serverList) { + if ("01".equals(serviceIns.getId())) { + serverNum1++; + } else if ("02".equals(serviceIns.getId())) { + serverNum2++; + } + } + } + Assertions.assertTrue((serverNum2 + serverNum1) == serverNum1); + } + + @Test + public void testDistributeRouteAndFallbackHaveSame() { + List list = initServiceList(TARGET_SERVICE_NAME_ROUTE_FALLBACK); + HashMap header = new HashMap<>(); + header.put("canary", "canary"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(header); + List listOfServers = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME_ROUTE_FALLBACK, governanceRequest, routerDistributor); + Assertions.assertNotNull(listOfServers); + for (ServiceIns server : listOfServers) { + Assertions.assertEquals(TARGET_SERVICE_NAME_ROUTE_FALLBACK, server.getServerName()); + } + int serverNum1 = 0; + int serverNum2 = 0; + + for (int i = 0; i < 20; i++) { + List serverList = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME_ROUTE_FALLBACK, governanceRequest, routerDistributor); + for (ServiceIns serviceIns : serverList) { + if ("01".equals(serviceIns.getId())) { + serverNum1++; + } else if ("02".equals(serviceIns.getId())) { + serverNum2++; + } + } + } + boolean flag = false; + if (Math.round(serverNum1 * 1.0 / serverNum2) == 3) { + flag = true; + } + Assertions.assertTrue(flag); + } + + List initServiceList(String serviceName) { + ServiceIns serviceIns1 = new ServiceIns("01", serviceName); + ServiceIns serviceIns2 = new ServiceIns("02", serviceName); + serviceIns1.setVersion("1.0"); + serviceIns2.setVersion("2.0"); + serviceIns1.addTags("x-group", "red"); + serviceIns2.addTags("x-group", "green"); + List list = new ArrayList<>(); + list.add(serviceIns1); + list.add(serviceIns2); + return list; + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorGlobalConfigTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorGlobalConfigTest.java new file mode 100644 index 00000000000..89a54d76979 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorGlobalConfigTest.java @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.router; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; +import org.apache.servicecomb.governance.event.GovernanceEventManager; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.router.cache.RouterRuleCache; +import org.apache.servicecomb.router.distribute.RouterDistributor; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.Environment; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) +public class RouterDistributorGlobalConfigTest { + private static final String TARGET_SERVICE_NAME = "test_server"; + + public static final String CONFIG_KEY = RouterRuleCache.ROUTE_RULE_PREFIX + TARGET_SERVICE_NAME; + + private static final String RULE_STRING = "" + + " - precedence: 1\n" + + " match:\n" + + " headers: #header匹配\n" + + " appId:\n" + + " exact: \"01\"\n" + + " userId:\n" + + " exact: \"02\"\n" + + " route:\n" + + " - weight: 100\n" + + " tags:\n" + + " version: 2.0\n" + + " - precedence: 2\n" + + " match:\n" + + " headers: #header匹配\n" + + " appId:\n" + + " exact: \"01\"\n" + + " userId:\n" + + " exact: \"03\"\n" + + " route:\n" + + " - weight: 100\n" + + " tags:\n" + + " version: 1.0\n"; + + private Environment environment; + + private RouterFilter routerFilter; + + private RouterDistributor testDistributor; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + public void setRouterFilter(RouterFilter routerFilter) { + this.routerFilter = routerFilter; + } + + @Autowired + public void setTestDistributor(RouterDistributor testDistributor) { + this.testDistributor = testDistributor; + } + + public RouterDistributorGlobalConfigTest() { + } + + private final Map dynamicValues = new HashMap<>(); + + @BeforeEach + public void setUp() { + ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; + + if (configurableEnvironment.getPropertySources().contains("testDynamicChange")) { + configurableEnvironment.getPropertySources().remove("testDynamicChange"); + } + + configurableEnvironment.getPropertySources() + .addFirst(new EnumerablePropertySource>("testDynamicChange", dynamicValues) { + @Override + public Object getProperty(String s) { + return this.getSource().get(s); + } + + @Override + public String[] getPropertyNames() { + return this.getSource().keySet().toArray(new String[0]); + } + }); + + dynamicValues.put(RouterRuleCache.GLOBAL_ROUTE_RULE_KEY, RULE_STRING); + postConfigurationChangedEvent(RouterRuleCache.GLOBAL_ROUTE_RULE_KEY); + } + + @Test + public void testUseGlobalRouteRule() { + Map headers = new HashMap<>(); + headers.put("userId", "03"); + headers.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); + + List serverList = new ArrayList<>(); + ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); + ins1.setVersion("1.0"); + ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); + ins2.addTags("app", "a"); + ins2.setVersion("2.0"); + serverList.add(ins1); + serverList.add(ins2); + + List resultServerList = mainFilter(serverList, governanceRequest); + Assertions.assertEquals(1, resultServerList.size()); + Assertions.assertEquals("01", resultServerList.get(0).getId()); + } + + @Test + public void testUseProviderRouteRule() { + String rule = "" + + " - precedence: 1\n" + + " match:\n" + + " headers: #header匹配\n" + + " appId:\n" + + " exact: \"01\"\n" + + " userId:\n" + + " exact: \"03\"\n" + + " route:\n" + + " - weight: 100\n" + + " tags:\n" + + " version: 2.0\n" + + " - precedence: 2\n" + + " match:\n" + + " headers: #header匹配\n" + + " appId:\n" + + " exact: \"01\"\n" + + " userId:\n" + + " exact: \"02\"\n" + + " route:\n" + + " - weight: 100\n" + + " tags:\n" + + " version: 1.0\n"; + dynamicValues.put(CONFIG_KEY, rule); + postConfigurationChangedEvent(CONFIG_KEY); + + Map headers = new HashMap<>(); + headers.put("userId", "03"); + headers.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); + + List serverList = new ArrayList<>(); + ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); + ins1.setVersion("1.0"); + ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); + ins2.addTags("app", "a"); + ins2.setVersion("2.0"); + serverList.add(ins1); + serverList.add(ins2); + + List resultServerList = mainFilter(serverList, governanceRequest); + Assertions.assertEquals(1, resultServerList.size()); + Assertions.assertEquals("02", resultServerList.get(0).getId()); + } + + private List mainFilter(List serverList, + GovernanceRequestExtractor governanceRequestExtractor) { + return routerFilter + .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, governanceRequestExtractor, + testDistributor); + } + + private void postConfigurationChangedEvent(String changKey) { + Set changedKeys = new HashSet<>(); + changedKeys.add(changKey); + GovernanceConfigurationChangedEvent newEvent = new GovernanceConfigurationChangedEvent(changedKeys); + GovernanceEventManager.post(newEvent); + } +} diff --git a/governance/src/test/resources/META-INF/spring/bean.xml b/governance/src/test/resources/META-INF/spring/bean.xml index f9382441770..5ff6c6e05cc 100644 --- a/governance/src/test/resources/META-INF/spring/bean.xml +++ b/governance/src/test/resources/META-INF/spring/bean.xml @@ -23,4 +23,5 @@ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> + diff --git a/governance/src/test/resources/application.yaml b/governance/src/test/resources/application.yaml index e6ba6c73b5e..ce981ac1ea2 100644 --- a/governance/src/test/resources/application.yaml +++ b/governance/src/test/resources/application.yaml @@ -93,12 +93,17 @@ servicecomb: matches: - apiPath: prefix: "/mapper" + demo-mapper-query: | + matches: + - queries: + name: + exact: "bob" rateLimiting: demo-rateLimiting: | rate: 1 demo-rateLimiting-servicename: | rate: 1 - wrongIngored: | + wrongIgnored: | rate: 0 identifierRateLimiting: demo-rateLimiting: | @@ -126,7 +131,7 @@ servicecomb: retry: demo-retry: | maxAttempts: 3 - wrongIngored: | + wrongIgnored: | maxAttempts: -1 circuitBreaker: demo-circuitBreaker: | @@ -134,7 +139,7 @@ servicecomb: slidingWindowSize: 2 slidingWindowType: COUNT_BASED services: myself:1.0,other - wrongIngored: | + wrongIgnored: | rules: match: demo-circuitBreaker.xx minimumNumberOfCalls: -1 @@ -153,7 +158,7 @@ servicecomb: maxConcurrentCalls: 1 maxWaitDuration: 3000 services: other:1.0 - wrongIngored: | + wrongIgnored: | rules: match: demo-bulkhead.xx maxWaitDuration: -1 @@ -186,7 +191,7 @@ servicecomb: type: abort percentage: 50 errorCode: 500 - wrongIngored: | + wrongIgnored: | delayTime: -1 type: ERROR faultInjection2: @@ -203,6 +208,9 @@ servicecomb: target: host: 127.0.0.1 port: 8080 + demo-mapper-query: | + target: + user-id: $Q{name} mapper2: demo-mapper: | target: @@ -215,10 +223,8 @@ servicecomb: headers: # header 匹配 region: # 如果配置了多个 header,那么所有的 header 规则都必须和请求匹配 exact: 'providerRegion' - caseInsensitive: false # 不区分大小写 type: regex: gray_[a-z]+ # java 正则表达式匹配 - caseInsensitive: true # 区分大小写 route: # 路由规则 - weight: 20 # 权重值 tags: @@ -235,3 +241,58 @@ servicecomb: - weight: 80 tags: x-group: green + + test_server2: | # 服务名 + - precedence: 1 + route: + - weight: 20 + tags: + x-group: red + + test_server3: | # 服务名 + - precedence: 2 + match: + headers: + canary: + exact: 'canary' + route: + - weight: 20 + tags: + version: 1.0.0 + - weight: 80 + tags: + version: 2.0.0 + test_server4: | # 服务名 + - precedence: 2 + match: + headers: + canary: + exact: 'canary' + route: + - weight: 20 + tags: + version: 1.0.0 + - weight: 80 + tags: + version: 2.0.0 + fallback: + - weight: 100 + tags: + x-group: red + test_server5: | # 服务名 + - precedence: 2 + match: + headers: + canary: + exact: 'canary' + route: + - weight: 50 + tags: + x-group: red + fallback: + - weight: 50 + tags: + x-group: red + - weight: 50 + tags: + x-group: green diff --git a/handlers/handler-fault-injection/pom.xml b/handlers/handler-fault-injection/pom.xml index 1896581a3a7..825c63c2270 100755 --- a/handlers/handler-fault-injection/pom.xml +++ b/handlers/handler-fault-injection/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb handlers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT handler-fault-injection Java Chassis::Handlers::Fault Injection diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerAbortFaultFilter.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerAbortFaultFilter.java index 5c2b9d4e8f5..e4eef91ef2f 100644 --- a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerAbortFaultFilter.java +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerAbortFaultFilter.java @@ -18,20 +18,19 @@ import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ConsumerAbortFaultFilter implements ConsumerFilter { +public class ConsumerAbortFaultFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerAbortFaultFilter.class); private static final String SUCCESS_RESPONSE = "success"; @@ -39,11 +38,10 @@ public class ConsumerAbortFaultFilter implements ConsumerFilter { public static final String ABORTED_ERROR_MSG = "aborted by fault inject"; @Override - public int getOrder(InvocationType invocationType, String microservice) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1020; } - @Nonnull @Override public String getName() { return "consumer-abort-fault"; diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerDelayFaultFilter.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerDelayFaultFilter.java index 97e782fc486..e8c0f6ffd67 100644 --- a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerDelayFaultFilter.java +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerDelayFaultFilter.java @@ -18,13 +18,12 @@ import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,15 +31,14 @@ import io.vertx.core.Context; import io.vertx.core.Vertx; -public class ConsumerDelayFaultFilter implements ConsumerFilter { +public class ConsumerDelayFaultFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerDelayFaultFilter.class); @Override - public int getOrder(InvocationType invocationType, String microservice) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1030; } - @Nonnull @Override public String getName() { return "consumer-delay-fault"; diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfig.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfig.java deleted file mode 100755 index 76b8e94ba1d..00000000000 --- a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfig.java +++ /dev/null @@ -1,66 +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.servicecomb.faultinjection; - -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.config.DynamicIntProperty; -import com.netflix.config.DynamicPropertyFactory; - -/** - * Handles the fault injection configuration read from micro service file/config - * center. - */ -public final class FaultInjectionConfig { - - private static final Logger LOGGER = LoggerFactory.getLogger(FaultInjectionConfig.class); - - // key is configuration parameter. - private static final Map cfgCallback = new ConcurrentHashMapEx<>(); - - public static int getConfigVal(String config, int defaultValue) { - DynamicIntProperty dynamicIntProperty = DynamicPropertyFactory.getInstance().getIntProperty(config, - defaultValue); - - cfgCallback.computeIfAbsent(config, key -> { - dynamicIntProperty.addCallback(() -> { - int newValue = dynamicIntProperty.get(); - String cfgName = dynamicIntProperty.getName(); - - //store the value in config center map and check for next requests. - FaultInjectionUtil.setConfigCenterValue(cfgName, new AtomicInteger(newValue)); - LOGGER.info("{} changed to {}", cfgName, newValue); - }); - return config; - }); - - return dynamicIntProperty.get(); - } - - @VisibleForTesting - static Map getCfgCallback() { - return cfgCallback; - } - -} diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfiguration.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfiguration.java index d954ab336bf..600ed5dc9f6 100644 --- a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfiguration.java +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfiguration.java @@ -29,12 +29,12 @@ public class FaultInjectionConfiguration { public static final String FAULT_INJECTION_ENABLED = FAULT_INJECTION_PREFIX + ".enabled"; @Bean - public ConsumerAbortFaultFilter consumerAbortFaultFilter() { + public ConsumerAbortFaultFilter scbConsumerAbortFaultFilter() { return new ConsumerAbortFaultFilter(); } @Bean - public ConsumerDelayFaultFilter consumerDelayFaultFilter() { + public ConsumerDelayFaultFilter scbConsumerDelayFaultFilter() { return new ConsumerDelayFaultFilter(); } } diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java index 220fdb4ca4c..67c3ab86939 100755 --- a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java @@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import com.google.common.annotations.VisibleForTesting; @@ -38,7 +39,7 @@ public class FaultInjectionUtil { private FaultInjectionUtil() { } - // key is config paramter + // key is config parameter private static final Map configCenterValue = new ConcurrentHashMapEx<>(); /** @@ -120,7 +121,7 @@ private static int getConfigValue(String config) { return cfgMap.get(config).get(); } - value = FaultInjectionConfig.getConfigVal(config, FAULT_INJECTION_DEFAULT_VALUE); + value = LegacyPropertyFactory.getIntProperty(config, FAULT_INJECTION_DEFAULT_VALUE); return value; } diff --git a/handlers/handler-fault-injection/src/main/resources/META-INF/spring.factories b/handlers/handler-fault-injection/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 56b46eb4cef..00000000000 --- a/handlers/handler-fault-injection/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,19 +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. -## --------------------------------------------------------------------------- - -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.faultinjection.FaultInjectionConfiguration diff --git a/handlers/handler-fault-injection/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/handlers/handler-fault-injection/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..b58acc214f3 --- /dev/null +++ b/handlers/handler-fault-injection/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.faultinjection.FaultInjectionConfiguration diff --git a/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestConsumerAbortFaultFilter.java b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestConsumerAbortFaultFilter.java index ecb5bf6ea7b..d44fd849b2f 100644 --- a/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestConsumerAbortFaultFilter.java +++ b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestConsumerAbortFaultFilter.java @@ -24,7 +24,7 @@ import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.common.Holder; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -34,16 +34,55 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; - -import com.netflix.config.DynamicProperty; +import org.springframework.core.env.Environment; public class TestConsumerAbortFaultFilter { private Invocation invocation; + private Environment environment; + @BeforeEach public void before() { - ArchaiusUtils.resetConfig(); - FaultInjectionConfig.getCfgCallback().clear(); + environment = Mockito.mock(Environment.class); + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer.carts6.schemas.testSchema4.operations.sayBye4" + + ".policy.fault.protocols.rest.abort.percent", + int.class, -1)).thenReturn(-1); + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer.carts6.schemas.testSchema4.operations.sayBye4" + + ".policy.fault.protocols.rest.abort.httpStatus", + int.class, -1)).thenReturn(-1); + + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer.carts6.schemas.testSchema4" + + ".policy.fault.protocols.rest.abort.percent", + int.class, -1)).thenReturn(-1); + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer.carts6.schemas.testSchema4" + + ".policy.fault.protocols.rest.abort.httpStatus", + int.class, -1)).thenReturn(-1); + + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer.carts6" + + ".policy.fault.protocols.rest.abort.percent", + int.class, -1)).thenReturn(-1); + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer.carts6" + + ".policy.fault.protocols.rest.abort.httpStatus", + int.class, -1)).thenReturn(-1); + + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer._global" + + ".policy.fault.protocols.rest.abort.percent", + int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer._global" + + ".policy.fault.protocols.rest.abort.httpStatus", + int.class, -1)) + .thenReturn(-1); + FaultInjectionUtil.getConfigCenterValue().clear(); invocation = Mockito.mock(Invocation.class); @@ -58,7 +97,6 @@ public void before() { @AfterEach public void after() { - ArchaiusUtils.resetConfig(); } @AfterAll @@ -68,17 +106,16 @@ public static void classTeardown() { @Test public void injectFaultError() { - ArchaiusUtils - .setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus", "421"); - ArchaiusUtils - .setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.abort.percent", "100"); - - Assertions.assertEquals("421", DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus") - .getString()); - Assertions.assertEquals("100", DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.abort.percent") - .getString()); + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer._global" + + ".policy.fault.protocols.rest.abort.httpStatus", + int.class, -1)) + .thenReturn(421); + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer._global" + + ".policy.fault.protocols.rest.abort.percent", + int.class, -1)) + .thenReturn(100); ConsumerAbortFaultFilter abortFault = new ConsumerAbortFaultFilter(); @@ -100,17 +137,6 @@ public void injectFaultError() { @Test public void injectFaultNoError() throws Exception { - ArchaiusUtils - .setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus", "421"); - ArchaiusUtils.setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.abort.percent", "0"); - - Assertions.assertEquals("421", DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus") - .getString()); - Assertions.assertEquals("0", DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.abort.percent") - .getString()); - ConsumerAbortFaultFilter abortFault = new ConsumerAbortFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); Mockito.when(filterNode.onFilter(invocation)) @@ -121,13 +147,6 @@ public void injectFaultNoError() throws Exception { @Test public void injectFaultNoPercentageConfig() throws Exception { - ArchaiusUtils - .setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.abort.percent", null); - - Assertions.assertNull(DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.abort.percent") - .getString()); - ConsumerAbortFaultFilter abortFault = new ConsumerAbortFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); Mockito.when(filterNode.onFilter(invocation)) @@ -138,13 +157,6 @@ public void injectFaultNoPercentageConfig() throws Exception { @Test public void injectFaultNoErrorCodeConfig() throws Exception { - ArchaiusUtils - .setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.abort.percent", "10"); - - Assertions.assertEquals("10", DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.abort.percent") - .getString()); - ConsumerAbortFaultFilter abortFault = new ConsumerAbortFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); Mockito.when(filterNode.onFilter(invocation)) diff --git a/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestConsumerDelayFaultFilter.java b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestConsumerDelayFaultFilter.java index d1edf711aa7..381d3d01447 100644 --- a/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestConsumerDelayFaultFilter.java +++ b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestConsumerDelayFaultFilter.java @@ -22,7 +22,7 @@ import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.AfterAll; @@ -31,16 +31,56 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; - -import com.netflix.config.DynamicProperty; +import org.springframework.core.env.Environment; public class TestConsumerDelayFaultFilter { private Invocation invocation; + private Environment environment; + @BeforeEach public void before() { - ArchaiusUtils.resetConfig(); - FaultInjectionConfig.getCfgCallback().clear(); + environment = Mockito.mock(Environment.class); + LegacyPropertyFactory.setEnvironment(environment); + + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer.carts6.schemas.testSchema4.operations.sayBye4" + + ".policy.fault.protocols.rest.delay.fixedDelay", + int.class, -1)).thenReturn(-1); + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer.carts6.schemas.testSchema4.operations.sayBye4" + + ".policy.fault.protocols.rest.delay.percent", + int.class, -1)).thenReturn(-1); + + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer.carts6.schemas.testSchema4" + + ".policy.fault.protocols.rest.delay.fixedDelay", + int.class, -1)).thenReturn(-1); + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer.carts6.schemas.testSchema4" + + ".policy.fault.protocols.rest.delay.percent", + int.class, -1)).thenReturn(-1); + + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer.carts6" + + ".policy.fault.protocols.rest.delay.fixedDelay", + int.class, -1)).thenReturn(-1); + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer.carts6" + + ".policy.fault.protocols.rest.delay.percent", + int.class, -1)).thenReturn(-1); + + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer._global" + + ".policy.fault.protocols.rest.delay.fixedDelay", + int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty( + "servicecomb.governance.Consumer._global" + + ".policy.fault.protocols.rest.delay.percent", + int.class, -1)) + .thenReturn(-1); + FaultInjectionUtil.getConfigCenterValue().clear(); invocation = Mockito.mock(Invocation.class); @@ -55,7 +95,6 @@ public void before() { @AfterEach public void after() { - ArchaiusUtils.resetConfig(); } @AfterAll @@ -65,17 +104,15 @@ public static void classTeardown() { @Test public void injectFaultVertxDelay() throws Exception { - ArchaiusUtils - .setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay", "10"); - ArchaiusUtils - .setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", "100"); - - Assertions.assertEquals("10", DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay") - .getString()); - Assertions.assertEquals("100", DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent") - .getString()); + Mockito.when(environment.getProperty("servicecomb.governance.Consumer._global" + + ".policy.fault.protocols.rest.delay.fixedDelay", + int.class, -1)) + .thenReturn(10); + Mockito.when( + environment.getProperty("servicecomb.governance.Consumer._global" + + ".policy.fault.protocols.rest.delay.percent", + int.class, -1)) + .thenReturn(100); ConsumerDelayFaultFilter delayFault = new ConsumerDelayFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); @@ -87,17 +124,14 @@ public void injectFaultVertxDelay() throws Exception { @Test public void injectFaultSystemDelay() throws Exception { - ArchaiusUtils - .setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay", "10"); - ArchaiusUtils - .setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", "100"); - - Assertions.assertEquals("10", DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay") - .getString()); - Assertions.assertEquals("100", DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent") - .getString()); + Mockito.when( + environment.getProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay", + int.class, -1)) + .thenReturn(10); + Mockito.when( + environment.getProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", + int.class, -1)) + .thenReturn(100); ConsumerDelayFaultFilter delayFault = new ConsumerDelayFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); @@ -109,16 +143,14 @@ public void injectFaultSystemDelay() throws Exception { @Test public void injectFaultNotDelay() throws Exception { - ArchaiusUtils - .setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay", "10"); - ArchaiusUtils.setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", "0"); - - Assertions.assertEquals("10", DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay") - .getString()); - Assertions.assertEquals("0", DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent") - .getString()); + Mockito.when( + environment.getProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay", + int.class, -1)) + .thenReturn(10); + Mockito.when( + environment.getProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", + int.class, -1)) + .thenReturn(0); ConsumerDelayFaultFilter delayFault = new ConsumerDelayFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); @@ -130,13 +162,6 @@ public void injectFaultNotDelay() throws Exception { @Test public void injectFaultNoPercentageConfig() throws Exception { - ArchaiusUtils - .setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", null); - - Assertions.assertNull(DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent") - .getString()); - ConsumerDelayFaultFilter delayFault = new ConsumerDelayFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); Mockito.when(filterNode.onFilter(invocation)) @@ -147,12 +172,10 @@ public void injectFaultNoPercentageConfig() throws Exception { @Test public void injectFaultNoDelayMsConfig() throws Exception { - ArchaiusUtils - .setProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", "10"); - - Assertions.assertEquals("10", DynamicProperty - .getInstance("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent") - .getString()); + Mockito.when( + environment.getProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", + int.class, -1)) + .thenReturn(10); ConsumerDelayFaultFilter delayFault = new ConsumerDelayFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); diff --git a/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectConfig.java b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectConfig.java deleted file mode 100644 index 811e736ea1e..00000000000 --- a/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectConfig.java +++ /dev/null @@ -1,62 +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.servicecomb.faultinjection; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -/** - * Tests the fault injection configuration. - */ -public class TestFaultInjectConfig { - FaultInjectionConfig faultCfg; - - FaultInjectionConst faultConst; - - FaultParam faultParam; - - @BeforeEach - public void setUp() throws Exception { - faultParam = new FaultParam(); - } - - @AfterEach - public void tearDown() throws Exception { - faultCfg = null; - faultConst = null; - faultParam = null; - } - - @Test - public void testFaultInjectConfig() throws Exception { - int val = FaultInjectionConfig.getConfigVal("servicecomb.servicecomb.handler.consumer.faultinject.config", 0); - Assertions.assertEquals(0, val); - } - - @Test - public void testConstants() { - Assertions.assertEquals("servicecomb.governance.Consumer.", FaultInjectionConst.CONSUMER_FAULTINJECTION); - Assertions.assertEquals("policy.fault.protocols.", FaultInjectionConst.CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS); - Assertions.assertEquals(-1, FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE); - Assertions.assertEquals("servicecomb.governance.Consumer._global.", - FaultInjectionConst.CONSUMER_FAULTINJECTION_GLOBAL); - Assertions.assertEquals(-1, FaultInjectionConst.FAULT_INJECTION_ERROR); - } -} diff --git a/handlers/handler-flowcontrol-qps/pom.xml b/handlers/handler-flowcontrol-qps/pom.xml index 7001cb8d5bc..eb5d63a0993 100644 --- a/handlers/handler-flowcontrol-qps/pom.xml +++ b/handlers/handler-flowcontrol-qps/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb handlers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT handler-flowcontrol-qps Java Chassis::Handlers::Flow Control QPS diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/Config.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/Config.java index 807842850d2..8325352d726 100644 --- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/Config.java +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/Config.java @@ -17,14 +17,10 @@ package org.apache.servicecomb.qps; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.config.DynamicBooleanProperty; -import com.netflix.config.DynamicPropertyFactory; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; public final class Config { - private static final Logger LOGGER = LoggerFactory.getLogger(Config.class); + public static final String CONFIG_PREFIX = "servicecomb.flowcontrol."; public static final String STRATEGY_KEY = "servicecomb.flowcontrol.strategy"; @@ -56,29 +52,15 @@ public final class Config { public static Config INSTANCE = new Config(); - private final DynamicBooleanProperty consumerEnabled = - DynamicPropertyFactory.getInstance().getBooleanProperty(CONSUMER_ENABLED, true); - - private final DynamicBooleanProperty providerEnabled = - DynamicPropertyFactory.getInstance().getBooleanProperty(PROVIDER_ENABLED, true); - public Config() { - consumerEnabled.addCallback(() -> { - boolean newValue = consumerEnabled.get(); - LOGGER.info("{} changed from {} to {}", CONSUMER_ENABLED, consumerEnabled, newValue); - }); - - providerEnabled.addCallback(() -> { - boolean newValue = providerEnabled.get(); - LOGGER.info("{} changed from {} to {}", PROVIDER_ENABLED, providerEnabled, newValue); - }); + } public boolean isConsumerEnabled() { - return consumerEnabled.get(); + return LegacyPropertyFactory.getBooleanProperty(CONSUMER_ENABLED, true); } public boolean isProviderEnabled() { - return providerEnabled.get(); + return LegacyPropertyFactory.getBooleanProperty(PROVIDER_ENABLED, true); } } diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerFlowControlFilter.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerFlowControlFilter.java index af259bb13c8..72828b35ccf 100644 --- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerFlowControlFilter.java +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerFlowControlFilter.java @@ -19,21 +19,25 @@ import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; -public class ConsumerFlowControlFilter implements ConsumerFilter { - private final QpsControllerManager qpsControllerMgr = new QpsControllerManager(false); +public class ConsumerFlowControlFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { + private final QpsControllerManager qpsControllerMgr; + + public ConsumerFlowControlFilter(Environment environment) { + qpsControllerMgr = new QpsControllerManager(false, environment); + } @VisibleForTesting public QpsControllerManager getQpsControllerMgr() { @@ -41,11 +45,10 @@ public QpsControllerManager getQpsControllerMgr() { } @Override - public int getOrder(InvocationType invocationType, String microservice) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1990; } - @Nonnull @Override public String getName() { return "consumer-flow-control"; diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/FlowControlQpsConfiguration.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/FlowControlQpsConfiguration.java index de13eae570e..d5fd7702cd1 100644 --- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/FlowControlQpsConfiguration.java +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/FlowControlQpsConfiguration.java @@ -19,22 +19,23 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; @Configuration @ConditionalOnProperty(value = FlowControlQpsConfiguration.FLOW_CONTROL_ENABLED, - havingValue = "true", matchIfMissing = true) + havingValue = "true") public class FlowControlQpsConfiguration { public static final String FLOW_CONTROL_PREFIX = "servicecomb.flowcontrol"; public static final String FLOW_CONTROL_ENABLED = FLOW_CONTROL_PREFIX + ".enabled"; @Bean - public ProviderFlowControlFilter providerFlowControlFilter() { - return new ProviderFlowControlFilter(); + public ProviderFlowControlFilter scbProviderFlowControlFilter(Environment environment) { + return new ProviderFlowControlFilter(environment); } @Bean - public ConsumerFlowControlFilter consumerFlowControlFilter() { - return new ConsumerFlowControlFilter(); + public ConsumerFlowControlFilter scbConsumerFlowControlFilter(Environment environment) { + return new ConsumerFlowControlFilter(environment); } } diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderFlowControlFilter.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderFlowControlFilter.java index a4840146d7a..00f78f126f1 100644 --- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderFlowControlFilter.java +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderFlowControlFilter.java @@ -19,29 +19,31 @@ import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.filter.ProducerFilter; -import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; -public class ProviderFlowControlFilter implements ProducerFilter { - private final QpsControllerManager qpsControllerMgr = new QpsControllerManager(true); +public class ProviderFlowControlFilter extends AbstractFilter implements ProviderFilter { + private final QpsControllerManager qpsControllerMgr; + + public ProviderFlowControlFilter(Environment environment) { + qpsControllerMgr = new QpsControllerManager(true, environment); + } @Override - public int getOrder(InvocationType invocationType, String microservice) { - return Filter.PRODUCER_SCHEDULE_FILTER_ORDER - 1990; + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1990; } - @Nonnull @Override public String getName() { return "provider-flow-control"; @@ -53,7 +55,7 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne return nextNode.onFilter(invocation); } - String microserviceName = invocation.getContext(Const.SRC_MICROSERVICE); + String microserviceName = invocation.getContext(CoreConst.SRC_MICROSERVICE); QpsStrategy qpsStrategy = qpsControllerMgr.getOrCreate(microserviceName, invocation); if (qpsStrategy.isLimitNewRequest()) { CommonExceptionData errorData = new CommonExceptionData( diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsConst.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsConst.java index 6dae2f4133d..ab1713ec497 100644 --- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsConst.java +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsConst.java @@ -16,7 +16,7 @@ */ package org.apache.servicecomb.qps; -import javax.ws.rs.core.Response.StatusType; +import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.swagger.invocation.context.HttpStatus; diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsControllerManager.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsControllerManager.java index 436d6db1344..e4cf5e18457 100644 --- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsControllerManager.java +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsControllerManager.java @@ -17,25 +17,26 @@ package org.apache.servicecomb.qps; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.qps.strategy.AbstractQpsStrategy; import org.apache.servicecomb.qps.strategy.IStrategyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; -import com.netflix.config.DynamicProperty; +import com.google.common.eventbus.Subscribe; -public class QpsControllerManager { +public final class QpsControllerManager { private static final Logger LOGGER = LoggerFactory.getLogger(QpsControllerManager.class); public static final String SEPARATOR = "."; @@ -60,7 +61,10 @@ public class QpsControllerManager { private final String globalBucketKey; - public QpsControllerManager(boolean isProvider) { + private final Environment environment; + + public QpsControllerManager(boolean isProvider, Environment environment) { + this.environment = environment; if (isProvider) { limitKeyPrefix = Config.PROVIDER_LIMIT_KEY_PREFIX; bucketKeyPrefix = Config.PROVIDER_BUCKET_KEY_PREFIX; @@ -74,6 +78,19 @@ public QpsControllerManager(boolean isProvider) { } initGlobalQpsController(); + EventManager.register(this); + } + + @Subscribe + public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { + for (String changed : event.getChanged()) { + if (changed.startsWith(Config.CONFIG_PREFIX)) { + configQpsControllerMap.clear(); + qualifiedNameControllerMap.clear(); + initGlobalQpsController(); + break; + } + } } @VisibleForTesting @@ -172,74 +189,19 @@ private void createQpsControllerIfNotExist(String configKey) { } LOGGER.info("Create qpsController, configKey = [{}]", configKey); - DynamicProperty limitProperty = DynamicProperty.getInstance(limitKeyPrefix + configKey); - DynamicProperty bucketProperty = DynamicProperty.getInstance(bucketKeyPrefix + configKey); - DynamicProperty strategyProperty = DynamicProperty.getInstance(Config.STRATEGY_KEY); - AbstractQpsStrategy qpsStrategy = chooseStrategy(configKey, limitProperty.getLong(), - bucketProperty.getLong(), strategyProperty.getString()); - - strategyProperty.addCallback(() -> { - AbstractQpsStrategy innerQpsStrategy = chooseStrategy(configKey, limitProperty.getLong(), - bucketProperty.getLong(), strategyProperty.getString()); - configQpsControllerMap.put(configKey, innerQpsStrategy); - LOGGER.info("Global flow control strategy update, value = [{}]", - strategyProperty.getString()); - updateObjMap(); - }); - limitProperty.addCallback(() -> { - qpsStrategy.setQpsLimit(limitProperty.getLong()); - LOGGER.info("Qps limit updated, configKey = [{}], value = [{}]", configKey, - limitProperty.getString()); - updateObjMap(); - }); - bucketProperty.addCallback(() -> { - qpsStrategy.setBucketLimit(bucketProperty.getLong()); - LOGGER.info("bucket limit updated, configKey = [{}], value = [{}]", configKey, - bucketProperty.getString()); - updateObjMap(); - }); + AbstractQpsStrategy qpsStrategy = chooseStrategy(configKey, + environment.getProperty(limitKeyPrefix + configKey, Long.class), + environment.getProperty(bucketKeyPrefix + configKey, Long.class), + environment.getProperty(Config.STRATEGY_KEY)); configQpsControllerMap.put(configKey, qpsStrategy); } - protected void updateObjMap() { - Iterator> it = qualifiedNameControllerMap.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = it.next(); - AbstractQpsStrategy qpsStrategy = searchQpsController(entry.getKey()); - if (qpsStrategy == null) { - it.remove(); - continue; - } - if (qpsStrategy != entry.getValue()) { - entry.setValue(qpsStrategy); - LOGGER.info("QpsController updated, operationId = [{}], configKey = [{}], qpsLimit = [{}]", - entry.getKey(), qpsStrategy.getKey(), qpsStrategy.getQpsLimit()); - } - } - } - private void initGlobalQpsController() { - DynamicProperty globalLimitProperty = DynamicProperty.getInstance(globalLimitKey); - DynamicProperty globalBucketProperty = DynamicProperty.getInstance(globalBucketKey); - DynamicProperty globalStrategyProperty = DynamicProperty - .getInstance(Config.STRATEGY_KEY); - globalQpsStrategy = chooseStrategy(globalLimitKey, globalLimitProperty.getLong((long) Integer.MAX_VALUE), - globalBucketProperty.getLong(), globalStrategyProperty.getString()); - globalStrategyProperty.addCallback(() -> { - globalQpsStrategy = chooseStrategy(globalLimitKey, globalLimitProperty.getLong((long) Integer.MAX_VALUE), - globalBucketProperty.getLong(), globalStrategyProperty.getString()); - LOGGER.info("Global flow control strategy update, value = [{}]", - globalStrategyProperty.getString()); - }); - globalLimitProperty.addCallback(() -> { - globalQpsStrategy.setQpsLimit(globalLimitProperty.getLong((long) Integer.MAX_VALUE)); - LOGGER.info("Global qps limit update, value = [{}]", globalLimitProperty.getLong()); - }); - globalBucketProperty.addCallback(() -> { - globalQpsStrategy.setBucketLimit(globalBucketProperty.getLong()); - LOGGER.info("Global bucket limit update, value = [{}]", globalBucketProperty.getLong()); - }); + globalQpsStrategy = chooseStrategy(globalLimitKey, + environment.getProperty(globalLimitKey, Long.class, (long) Integer.MAX_VALUE), + environment.getProperty(globalBucketKey, Long.class), + environment.getProperty(Config.STRATEGY_KEY)); } private AbstractQpsStrategy chooseStrategy(String configKey, Long limit, Long bucket, diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/FixedWindowStrategy.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/FixedWindowStrategy.java index 65204c3a304..c95c1ab2cba 100644 --- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/FixedWindowStrategy.java +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/FixedWindowStrategy.java @@ -52,7 +52,7 @@ public boolean isLimitNewRequest() { msCycleBegin = msNow; } - // Configuration update and use is at the situation of multi-threaded concurrency + // Configuration update and use is at the situation of multi-thread concurrency // It is possible that operation level updated to null,but schema level or microservice level does not updated boolean isLimitRequest = newCount - lastRequestCount >= this.getQpsLimit(); if (isLimitRequest){ diff --git a/handlers/handler-flowcontrol-qps/src/main/resources/META-INF/spring.factories b/handlers/handler-flowcontrol-qps/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 6849114170b..00000000000 --- a/handlers/handler-flowcontrol-qps/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,18 +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. -# -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.qps.FlowControlQpsConfiguration diff --git a/handlers/handler-flowcontrol-qps/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/handlers/handler-flowcontrol-qps/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..731cf581a05 --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.qps.FlowControlQpsConfiguration diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java index 8554968aa83..e2b5f79fa69 100644 --- a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java +++ b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java @@ -17,27 +17,36 @@ package org.apache.servicecomb.qps; +import java.util.HashMap; + +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.qps.strategy.AbstractQpsStrategy; 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 org.mockito.Mockito; +import org.springframework.core.env.Environment; public class QpsControllerManagerTest { + static Environment environment = Mockito.mock(Environment.class); @BeforeEach public void beforeTest() { - ArchaiusUtils.resetConfig(); + Mockito.when(environment.getProperty(Config.PROVIDER_LIMIT_KEY_GLOBAL, Long.class, (long) Integer.MAX_VALUE)) + .thenReturn((long) Integer.MAX_VALUE); + Mockito.when(environment.getProperty(Config.CONSUMER_LIMIT_KEY_GLOBAL, Long.class, (long) Integer.MAX_VALUE)) + .thenReturn((long) Integer.MAX_VALUE); } @AfterEach public void afterTest() { - ArchaiusUtils.resetConfig(); + } @Test @@ -48,7 +57,7 @@ public void testGetOrCreate() { Mockito.when(invocation.getSchemaId()).thenReturn("server"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); - QpsControllerManager testQpsControllerManager = new QpsControllerManager(false); + QpsControllerManager testQpsControllerManager = new QpsControllerManager(false, environment); initTestQpsControllerManager(false, testQpsControllerManager, invocation, operationMeta); // pojo @@ -64,12 +73,23 @@ public void testGetOrCreate() { Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); - ArchaiusUtils.setProperty("servicecomb.flowcontrol.Consumer.qps.limit.poj.server", 10000); + Mockito.when(environment.getProperty("servicecomb.flowcontrol.Consumer.qps.limit.poj.server", + Long.class)).thenReturn(Long.valueOf(10000)); + HashMap updated = new HashMap<>(); + updated.put("servicecomb.flowcontrol.Consumer.qps.limit.poj.server", Long.valueOf(10000)); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); + qpsStrategy = testQpsControllerManager.getOrCreate("poj", invocation); Assertions.assertEquals("poj.server", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(((AbstractQpsStrategy) qpsStrategy).getQpsLimit(), (Long) 10000L); - ArchaiusUtils.setProperty("servicecomb.flowcontrol.Consumer.qps.limit.poj.server.test", 20000); + InMemoryDynamicPropertiesSource.update("servicecomb.flowcontrol.Consumer.qps.limit.poj.server.test", 20000); + Mockito.when(environment.getProperty("servicecomb.flowcontrol.Consumer.qps.limit.poj.server.test", + Long.class)).thenReturn(Long.valueOf(20000)); + updated = new HashMap<>(); + updated.put("servicecomb.flowcontrol.Consumer.qps.limit.poj.server.test", Long.valueOf(20000)); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); + qpsStrategy = testQpsControllerManager.getOrCreate("poj", invocation); Assertions.assertEquals("poj.server.test", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(((AbstractQpsStrategy) qpsStrategy).getQpsLimit(), (Long) 20000L); @@ -85,10 +105,15 @@ public void testGetOrCreateWithGlobalConfig() { Mockito.when(invocation.getSchemaId()).thenReturn("server"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); - QpsControllerManager testQpsControllerManager = new QpsControllerManager(true); + QpsControllerManager testQpsControllerManager = new QpsControllerManager(true, environment); // global - setConfig(Config.PROVIDER_LIMIT_KEY_GLOBAL, 50); + Mockito.when(environment.getProperty(Config.PROVIDER_LIMIT_KEY_GLOBAL, Long.class, + (long) Integer.MAX_VALUE)).thenReturn(50L); + HashMap updated = new HashMap<>(); + updated.put(Config.PROVIDER_LIMIT_KEY_GLOBAL, 50L); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); + QpsStrategy qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(50, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); @@ -121,7 +146,7 @@ public void testQualifiedNameKey() { Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); Mockito.when(invocation.getSchemaId()).thenReturn("schema"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("schema.opr"); - QpsControllerManager qpsControllerManager = new QpsControllerManager(true); + QpsControllerManager qpsControllerManager = new QpsControllerManager(true, environment); QpsStrategy qpsStrategy = qpsControllerManager.getOrCreate("service", invocation); Assertions.assertEquals("servicecomb.flowcontrol.Provider.qps.global.limit", ((AbstractQpsStrategy) qpsStrategy).getKey()); @@ -315,17 +340,16 @@ public static OperationMeta getMockOperationMeta(String microserviceName, String return operationMeta; } - public static void setConfig(String key, int value) { - ArchaiusUtils.setProperty(key, value); - } - private static void setConfigWithDefaultPrefix(boolean isProvider, String key, int value) { String configKey = Config.CONSUMER_LIMIT_KEY_PREFIX + key; if (isProvider) { configKey = Config.PROVIDER_LIMIT_KEY_PREFIX + key; } - ArchaiusUtils.setProperty(configKey, value); + Mockito.when(environment.getProperty(configKey, Long.class)).thenReturn(Long.valueOf(value)); + HashMap updated = new HashMap<>(); + updated.put(configKey, value); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); } private static void deleteConfigWithDefaultPrefix(boolean isProvider, String key) { @@ -334,7 +358,10 @@ private static void deleteConfigWithDefaultPrefix(boolean isProvider, String key configKey = Config.PROVIDER_LIMIT_KEY_PREFIX + key; } - ArchaiusUtils.setProperty(configKey, null); + Mockito.when(environment.getProperty(configKey, Long.class)).thenReturn(null); + HashMap updated = new HashMap<>(); + updated.put(configKey, null); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); } @Test @@ -345,7 +372,7 @@ public void testDeleteQpsController() { final String operationId = "add"; final String configKey = "springmvcClient.controller.add"; - QpsControllerManager testManager = new QpsControllerManager(true); + QpsControllerManager testManager = new QpsControllerManager(true, environment); Invocation testInvocation = getMockInvocation(microserviceName, schemaId, operationId); Mockito.when(testInvocation.getSchemaId()).thenReturn(schemaId); @@ -357,6 +384,8 @@ public void testDeleteQpsController() { QpsStrategy strategy2 = testManager.getOrCreate(microserviceName, testInvocation); - Assertions.assertEquals(strategy1, strategy2); + Assertions.assertEquals(((AbstractQpsStrategy) strategy1).getQpsLimit(), + ((AbstractQpsStrategy) strategy2).getQpsLimit()); + Assertions.assertEquals(((AbstractQpsStrategy) strategy1).getQpsLimit(), Long.valueOf(Integer.MAX_VALUE)); } } diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConfig.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConfig.java deleted file mode 100644 index ea9f47440a9..00000000000 --- a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConfig.java +++ /dev/null @@ -1,55 +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.servicecomb.qps; - -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class TestConfig { - @BeforeAll - public static void classSetup() { - ArchaiusUtils.resetConfig(); - } - - @AfterAll - public static void classTeardown() { - ArchaiusUtils.resetConfig(); - } - - @Test - public void testEnabled() { - Config.INSTANCE = new Config(); - - Assertions.assertTrue(Config.INSTANCE.isProviderEnabled()); - ArchaiusUtils.setProperty(Config.PROVIDER_ENABLED, false); - Assertions.assertFalse(Config.INSTANCE.isProviderEnabled()); - - ArchaiusUtils.setProperty(Config.PROVIDER_ENABLED, null); - Assertions.assertTrue(Config.INSTANCE.isProviderEnabled()); - - Assertions.assertTrue(Config.INSTANCE.isConsumerEnabled()); - ArchaiusUtils.setProperty(Config.CONSUMER_ENABLED, false); - Assertions.assertFalse(Config.INSTANCE.isConsumerEnabled()); - - ArchaiusUtils.setProperty(Config.CONSUMER_ENABLED, null); - Assertions.assertTrue(Config.INSTANCE.isConsumerEnabled()); - } -} diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestQpsStrategy.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestQpsStrategy.java index 23c228fab2e..f34b6c5a261 100644 --- a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestQpsStrategy.java +++ b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestQpsStrategy.java @@ -24,8 +24,8 @@ import org.junit.jupiter.api.Test; /** - * @Author GuoYl123 - * @Date 2020/7/16 + * @author GuoYl123 + * @since 2020/7/16 **/ public class TestQpsStrategy { diff --git a/handlers/handler-governance/README.md b/handlers/handler-governance/README.md index 3a9db2ff26a..b6a237f6510 100644 --- a/handlers/handler-governance/README.md +++ b/handlers/handler-governance/README.md @@ -1,3 +1,3 @@ -Enable governance features by handler. +Enable governance features by handler. -Governance can be enabled in any different process layer, enable it in handler is most portable way. \ No newline at end of file +Governance can be enabled in any different process layer, enable it in handler is most portable way. diff --git a/handlers/handler-governance/pom.xml b/handlers/handler-governance/pom.xml index b039e206156..7db533e562e 100644 --- a/handlers/handler-governance/pom.xml +++ b/handlers/handler-governance/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb handlers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT handler-governance diff --git a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceBulkheadFilter.java b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceBulkheadFilter.java index 25ae74d61df..1b007e111ad 100644 --- a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceBulkheadFilter.java +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceBulkheadFilter.java @@ -21,17 +21,15 @@ import java.util.concurrent.CompletionStage; import java.util.function.Supplier; -import javax.annotation.Nonnull; -import javax.ws.rs.core.Response.Status; - import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.InstanceBulkheadHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -43,8 +41,9 @@ import io.github.resilience4j.bulkhead.BulkheadFullException; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; +import jakarta.ws.rs.core.Response.Status; -public class ConsumerInstanceBulkheadFilter implements ConsumerFilter { +public class ConsumerInstanceBulkheadFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerInstanceBulkheadFilter.class); private final InstanceBulkheadHandler instanceBulkheadHandler; @@ -55,11 +54,10 @@ public ConsumerInstanceBulkheadFilter(InstanceBulkheadHandler instanceBulkheadHa } @Override - public int getOrder(InvocationType invocationType, String microservice) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1060; } - @Nonnull @Override public String getName() { return "instance-bulkhead"; diff --git a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationFilter.java b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationFilter.java index c3c0e6f75dd..e60d52fab18 100644 --- a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationFilter.java +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationFilter.java @@ -17,24 +17,22 @@ package org.apache.servicecomb.handler.governance; +import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Supplier; -import javax.annotation.Nonnull; -import javax.ws.rs.core.Response.Status; - import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.InstanceIsolationHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; -import org.apache.servicecomb.registry.api.MicroserviceKey; -import org.apache.servicecomb.registry.api.event.MicroserviceInstanceChangedEvent; -import org.apache.servicecomb.registry.api.event.ServiceCenterEventBus; -import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; +import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -46,23 +44,30 @@ import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; +import jakarta.ws.rs.core.Response.Status; -public class ConsumerInstanceIsolationFilter implements ConsumerFilter { +public class ConsumerInstanceIsolationFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerInstanceIsolationFilter.class); private final InstanceIsolationHandler instanceIsolationHandler; + private DiscoveryManager discoveryManager; + @Autowired public ConsumerInstanceIsolationFilter(InstanceIsolationHandler instanceIsolationHandler) { this.instanceIsolationHandler = instanceIsolationHandler; } + @Autowired + public void setDiscoveryManager(DiscoveryManager discoveryManager) { + this.discoveryManager = discoveryManager; + } + @Override - public int getOrder(InvocationType invocationType, String microservice) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1050; } - @Nonnull @Override public String getName() { return "instance-isolation"; @@ -74,9 +79,16 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne return CompletableFuture.failedFuture(new InvocationException(Status.INTERNAL_SERVER_ERROR, new CommonExceptionData("instance isolation should work after load balancer."))); } + + GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); + CircuitBreakerPolicy circuitBreakerPolicy = instanceIsolationHandler.matchPolicy(request); + if (circuitBreakerPolicy != null && circuitBreakerPolicy.isForceOpen()) { + return CompletableFuture.failedFuture(new InvocationException(Status.SERVICE_UNAVAILABLE, + new CommonExceptionData("Policy \" + circuitBreakerPolicy.getName() + \" forced open and deny requests\""))); + } + Supplier> next = createBusinessCompletionStageSupplier(invocation, nextNode); DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); - GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); addCircuitBreaker(dcs, request); @@ -90,7 +102,8 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne if (e instanceof CallNotPermittedException) { LOGGER.warn("instance isolation circuitBreaker is open by policy : {}", e.getMessage()); - ServiceCenterEventBus.getEventBus().post(createMicroserviceInstanceChangedEvent(invocation)); + discoveryManager.onInstanceIsolated(invocation.getEndpoint().getMicroserviceInstance(), + Duration.parse(circuitBreakerPolicy.getWaitDurationInOpenState()).toMillis()); // return 503 so that consumer can retry future.complete(Response.failResp(new InvocationException(Status.SERVICE_UNAVAILABLE, new CommonExceptionData("instance isolation circuitBreaker is open.")))); @@ -102,15 +115,6 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne return future; } - private Object createMicroserviceInstanceChangedEvent(Invocation invocation) { - MicroserviceInstanceChangedEvent event = new MicroserviceInstanceChangedEvent(); - MicroserviceKey key = new MicroserviceKey(); - key.setAppId(invocation.getAppId()); - key.setServiceName(invocation.getMicroserviceName()); - event.setKey(key); - return event; - } - private void addCircuitBreaker(DecorateCompletionStage dcs, GovernanceRequestExtractor request) { CircuitBreaker circuitBreaker = instanceIsolationHandler.getActuator(request); if (circuitBreaker != null) { diff --git a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/HandlerGovernanceConfiguration.java b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/HandlerGovernanceConfiguration.java index b4d8f487256..9223146a759 100644 --- a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/HandlerGovernanceConfiguration.java +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/HandlerGovernanceConfiguration.java @@ -34,29 +34,29 @@ public class HandlerGovernanceConfiguration { public static final String GOVERNANCE_ENABLED = GOVERNANCE_PREFIX + ".enabled"; @Bean - public ConsumerInstanceBulkheadFilter consumerInstanceBulkheadFilter( + public ConsumerInstanceBulkheadFilter scbConsumerInstanceBulkheadFilter( InstanceBulkheadHandler instanceBulkheadHandler) { return new ConsumerInstanceBulkheadFilter(instanceBulkheadHandler); } @Bean - public ConsumerInstanceIsolationFilter consumerInstanceIsolationFilter( + public ConsumerInstanceIsolationFilter scbConsumerInstanceIsolationFilter( InstanceIsolationHandler instanceIsolationHandler) { return new ConsumerInstanceIsolationFilter(instanceIsolationHandler); } @Bean - public ProviderBulkheadFilter providerBulkheadFilter(BulkheadHandler bulkheadHandler) { + public ProviderBulkheadFilter scbProviderBulkheadFilter(BulkheadHandler bulkheadHandler) { return new ProviderBulkheadFilter(bulkheadHandler); } @Bean - public ProviderCircuitBreakerFilter providerCircuitBreakerFilter(CircuitBreakerHandler circuitBreakerHandler) { + public ProviderCircuitBreakerFilter scbProviderCircuitBreakerFilter(CircuitBreakerHandler circuitBreakerHandler) { return new ProviderCircuitBreakerFilter(circuitBreakerHandler); } @Bean - public ProviderRateLimitingFilter providerRateLimitingFilter(RateLimitingHandler rateLimitingHandler) { + public ProviderRateLimitingFilter scbProviderRateLimitingFilter(RateLimitingHandler rateLimitingHandler) { return new ProviderRateLimitingFilter(rateLimitingHandler); } } diff --git a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderBulkheadFilter.java b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderBulkheadFilter.java index 935d1eb4901..a68ebf08fb1 100644 --- a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderBulkheadFilter.java +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderBulkheadFilter.java @@ -21,16 +21,14 @@ import java.util.concurrent.CompletionStage; import java.util.function.Supplier; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.filter.ProducerFilter; +import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.BulkheadHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -43,7 +41,7 @@ import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; -public class ProviderBulkheadFilter implements ProducerFilter { +public class ProviderBulkheadFilter extends AbstractFilter implements ProviderFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ProviderBulkheadFilter.class); private final BulkheadHandler bulkheadHandler; @@ -54,11 +52,10 @@ public ProviderBulkheadFilter(BulkheadHandler bulkheadHandler) { } @Override - public int getOrder(InvocationType invocationType, String microservice) { - return Filter.PRODUCER_SCHEDULE_FILTER_ORDER - 1880; + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1880; } - @Nonnull @Override public String getName() { return "provider-bulkhead"; diff --git a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderCircuitBreakerFilter.java b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderCircuitBreakerFilter.java index 04192b3f5c2..dbc0b8978c6 100644 --- a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderCircuitBreakerFilter.java +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderCircuitBreakerFilter.java @@ -21,16 +21,14 @@ import java.util.concurrent.CompletionStage; import java.util.function.Supplier; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.filter.ProducerFilter; +import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.CircuitBreakerHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -43,7 +41,7 @@ import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; -public class ProviderCircuitBreakerFilter implements ProducerFilter { +public class ProviderCircuitBreakerFilter extends AbstractFilter implements ProviderFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ProviderCircuitBreakerFilter.class); private final CircuitBreakerHandler circuitBreakerHandler; @@ -54,11 +52,10 @@ public ProviderCircuitBreakerFilter(CircuitBreakerHandler circuitBreakerHandler) } @Override - public int getOrder(InvocationType invocationType, String microservice) { - return Filter.PRODUCER_SCHEDULE_FILTER_ORDER - 1890; + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1890; } - @Nonnull @Override public String getName() { return "provider-circuit-breaker"; diff --git a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderRateLimitingFilter.java b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderRateLimitingFilter.java index a839ce56102..732fcf48ab5 100644 --- a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderRateLimitingFilter.java +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderRateLimitingFilter.java @@ -21,16 +21,15 @@ import java.util.concurrent.CompletionStage; import java.util.function.Supplier; -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.filter.ProducerFilter; +import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.RateLimitingHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -43,7 +42,7 @@ import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RequestNotPermitted; -public class ProviderRateLimitingFilter implements ProducerFilter { +public class ProviderRateLimitingFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ProviderRateLimitingFilter.class); private final RateLimitingHandler rateLimitingHandler; @@ -54,14 +53,13 @@ public ProviderRateLimitingFilter(RateLimitingHandler rateLimitingHandler) { } @Override - public int getOrder(InvocationType invocationType, String microservice) { - return Filter.PRODUCER_SCHEDULE_FILTER_ORDER - 1900; + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1900; } - @Nonnull @Override public String getName() { - return "provider-rate-limiting"; + return "rate-limiting"; } @Override diff --git a/handlers/handler-governance/src/main/resources/META-INF/spring.factories b/handlers/handler-governance/src/main/resources/META-INF/spring.factories deleted file mode 100644 index ef965633f0f..00000000000 --- a/handlers/handler-governance/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,19 +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. -## --------------------------------------------------------------------------- - -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.handler.governance.HandlerGovernanceConfiguration diff --git a/handlers/handler-governance/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/handlers/handler-governance/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..1e25fbf7679 --- /dev/null +++ b/handlers/handler-governance/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.handler.governance.HandlerGovernanceConfiguration diff --git a/handlers/handler-loadbalance/pom.xml b/handlers/handler-loadbalance/pom.xml index e89e1b72017..0fd42dce838 100644 --- a/handlers/handler-loadbalance/pom.xml +++ b/handlers/handler-loadbalance/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb handlers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT handler-loadbalance @@ -36,39 +36,13 @@ org.apache.servicecomb java-chassis-core - - com.netflix.ribbon - ribbon - - - com.netflix.ribbon - ribbon-archaius - - - com.netflix.ribbon - ribbon-core - - - com.netflix.ribbon - ribbon-loadbalancer - - - com.netflix.archaius - archaius-core - - - io.reactivex - rxjava - org.apache.commons commons-lang3 - - com.netflix.servo - servo-core - provided + io.github.resilience4j + resilience4j-all org.apache.servicecomb diff --git a/handlers/handler-loadbalance/readme.MD b/handlers/handler-loadbalance/readme.MD index 335e494ba88..53b4ef30d91 100644 --- a/handlers/handler-loadbalance/readme.MD +++ b/handlers/handler-loadbalance/readme.MD @@ -1,6 +1,6 @@ 参考: https://github.com/Netflix/ribbon/wiki/Working-with-load-balancers LoadBalancer -Rule -Ping +Rule +Ping ServerList ServerListFilter diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/Configuration.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/Configuration.java index 9becdbc149d..87a527c9aff 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/Configuration.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/Configuration.java @@ -19,67 +19,73 @@ import java.util.Map; -import org.apache.servicecomb.registry.config.ConfigurePropertyUtils; - -import com.netflix.config.DynamicPropertyFactory; +import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; /** * configuration items - * */ public final class Configuration { //// 2.1 configuration items public static final String ROOT = "servicecomb.loadbalance."; - public static final String SERVER_EXPIRED_IN_SECONDS = "servicecomb.loadbalance.stats.serverExpiredInSeconds"; - - public static final String TIMER_INTERVAL_IN_MILLIS = "servicecomb.loadbalance.stats.timerIntervalInMillis"; + public static final String RULE_STRATEGY_GLOBAL = "servicecomb.loadbalance.strategy.name"; public static final String RULE_STRATEGY_NAME = "strategy.name"; // 2.0 configuration items public static final String ROOT_20 = "ribbon."; - // retry configurations - public static final String RETRY_HANDLER = "retryHandler"; - - // SessionStickinessRule configruation + // SessionStickinessRule configuration public static final String SESSION_TIMEOUT_IN_SECONDS = "SessionStickinessRule.sessionTimeoutInSeconds"; public static final String SUCCESSIVE_FAILED_TIMES = "SessionStickinessRule.successiveFailedTimes"; - private static final double PERCENT = 100; - public static final String FILTER_ISOLATION = "isolation."; - public static final String FILTER_OPEN = "enabled"; - - public static final String FILTER_ERROR_PERCENTAGE = "errorThresholdPercentage"; - - public static final String FILTER_ENABLE_REQUEST = "enableRequestThreshold"; - - public static final String FILTER_RECOVER_IMMEDIATELY_WHEN_SUCCESS = "recoverImmediatelyWhenSuccess"; - - public static final String FILTER_SINGLE_TEST = "singleTestTime"; - public static final String FILTER_MAX_SINGLE_TEST_WINDOW = "maxSingleTestWindow"; - public static final String FILTER_MIN_ISOLATION_TIME = "minIsolationTime"; - - public static final String FILTER_CONTINUOUS_FAILURE_THRESHOLD = "continuousFailureThreshold"; - public static final String TRANSACTIONCONTROL_OPTIONS_PREFIX_PATTERN = "servicecomb.loadbalance.%s.transactionControl.options"; public static final Configuration INSTANCE = new Configuration(); + public record RuleType(int type, String value) { + public static final int TYPE_SCHEMA = 1; + + public static final int TYPE_OPERATION = 2; + + public String getValue() { + return value; + } + + public int getType() { + return type; + } + } + private Configuration() { } - public String getRuleStrategyName(String microservice) { - return getStringProperty(null, - ROOT + microservice + "." + RULE_STRATEGY_NAME, - ROOT + RULE_STRATEGY_NAME); + public RuleType getRuleStrategyName(Invocation invocation) { + String value = getStringProperty(null, ROOT + invocation.getMicroserviceName() + "." + + invocation.getSchemaId() + "." + invocation.getOperationName() + "." + RULE_STRATEGY_NAME); + if (value != null) { + return new RuleType(RuleType.TYPE_OPERATION, value); + } + value = getStringProperty(null, ROOT + invocation.getMicroserviceName() + "." + + invocation.getSchemaId() + "." + RULE_STRATEGY_NAME); + if (value != null) { + return new RuleType(RuleType.TYPE_SCHEMA, value); + } + value = getStringProperty(null, ROOT + invocation.getMicroserviceName() + "." + + RULE_STRATEGY_NAME); + if (value != null) { + return new RuleType(RuleType.TYPE_SCHEMA, value); + } + return new RuleType(RuleType.TYPE_SCHEMA, + getStringProperty("RoundRobin", RULE_STRATEGY_GLOBAL)); } public int getSessionTimeoutInSeconds(String microservice) { @@ -106,49 +112,6 @@ public int getSuccessiveFailedTimes(String microservice) { } } - public boolean isIsolationFilterOpen(String microservice) { - String p = getStringProperty("true", - ROOT + microservice + "." + FILTER_ISOLATION + FILTER_OPEN, - ROOT + FILTER_ISOLATION + FILTER_OPEN); - return Boolean.parseBoolean(p); - } - - public int getErrorThresholdPercentage(String microservice) { - final int defaultValue = 0; - String p = getStringProperty("0", - ROOT + microservice + "." + FILTER_ISOLATION + FILTER_ERROR_PERCENTAGE, - ROOT + FILTER_ISOLATION + FILTER_ERROR_PERCENTAGE); - try { - int result = Integer.parseInt(p); - if (result <= PERCENT && result > 0) { - return result; - } - return defaultValue; - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public int getEnableRequestThreshold(String microservice) { - return getThreshold(microservice, FILTER_ENABLE_REQUEST); - } - - public int getSingleTestTime(String microservice) { - final int defaultValue = 60000; - String p = getStringProperty("60000", - ROOT + microservice + "." + FILTER_ISOLATION + FILTER_SINGLE_TEST, - ROOT + FILTER_ISOLATION + FILTER_SINGLE_TEST); - try { - int result = Integer.parseInt(p); - if (result >= 0) { - return result; - } - return defaultValue; - } catch (NumberFormatException e) { - return defaultValue; - } - } - public int getMaxSingleTestWindow() { final int defaultValue = 60000; String p = getStringProperty(Integer.toString(defaultValue), @@ -164,58 +127,19 @@ public int getMaxSingleTestWindow() { } } - public int getMinIsolationTime(String microservice) { - final int defaultValue = 3000; // 3 seconds - String p = getStringProperty("3000", - ROOT + microservice + "." + FILTER_ISOLATION + FILTER_MIN_ISOLATION_TIME, - ROOT + FILTER_ISOLATION + FILTER_MIN_ISOLATION_TIME); - try { - int result = Integer.parseInt(p); - if (result >= 0) { - return result; - } - return defaultValue; - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public boolean isRecoverImmediatelyWhenSuccess(String microservice) { - String p = getStringProperty("true", - ROOT + microservice + "." + FILTER_ISOLATION + FILTER_RECOVER_IMMEDIATELY_WHEN_SUCCESS, - ROOT + FILTER_ISOLATION + FILTER_RECOVER_IMMEDIATELY_WHEN_SUCCESS); - return Boolean.parseBoolean(p); - } - public Map getFlowsplitFilterOptions(String microservice) { String keyPrefix = String.format(TRANSACTIONCONTROL_OPTIONS_PREFIX_PATTERN, microservice); - return ConfigurePropertyUtils.getPropertiesWithPrefix(keyPrefix); + return ConfigUtil.stringPropertiesWithPrefix(LegacyPropertyFactory.getEnvironment(), keyPrefix); } public static String getStringProperty(String defaultValue, String... keys) { String property; for (String key : keys) { - property = DynamicPropertyFactory.getInstance().getStringProperty(key, null).get(); + property = LegacyPropertyFactory.getStringProperty(key); if (property != null) { return property; } } return defaultValue; } - - public int getContinuousFailureThreshold(String microservice) { - return getThreshold(microservice, FILTER_CONTINUOUS_FAILURE_THRESHOLD); - } - - private int getThreshold(String microservice, String threshold) { - final int defaultValue = 5; - String p = getStringProperty("5", - ROOT + microservice + "." + FILTER_ISOLATION + threshold, - ROOT + FILTER_ISOLATION + threshold); - try { - return Integer.parseInt(p); - } catch (NumberFormatException e) { - return defaultValue; - } - } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsFactory.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsFactory.java index 342bb8cd6ad..dc4cd6b6082 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsFactory.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsFactory.java @@ -25,8 +25,4 @@ public interface ExtensionsFactory { default RuleExt createLoadBalancerRule(String ruleName) { return null; } - - default ServerListFilterExt createServerListFilter(String key, String value, Object... args) { - return null; - } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsManager.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsManager.java index 0d0245e2d22..2187f59e789 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsManager.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsManager.java @@ -18,26 +18,19 @@ import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class ExtensionsManager { - private static final Logger LOGGER = LoggerFactory.getLogger(ExtensionsManager.class); - private final List extensionsFactories; public ExtensionsManager(List extensionsFactories) { this.extensionsFactories = extensionsFactories; } - public RuleExt createLoadBalancerRule(String microservice) { + public RuleExt createLoadBalancerRule(String ruleStrategyName) { RuleExt rule = null; for (ExtensionsFactory factory : extensionsFactories) { - if (factory.isSupport(Configuration.RULE_STRATEGY_NAME, - Configuration.INSTANCE.getRuleStrategyName(microservice))) { - rule = factory.createLoadBalancerRule( - Configuration.INSTANCE.getRuleStrategyName(microservice)); + if (factory.isSupport(Configuration.RULE_STRATEGY_NAME, ruleStrategyName)) { + rule = factory.createLoadBalancerRule(ruleStrategyName); break; } } @@ -46,7 +39,6 @@ public RuleExt createLoadBalancerRule(String microservice) { rule = new RoundRobinRuleExt(); } - LOGGER.info("Using load balance rule {} for microservice {}.", rule.getClass().getName(), microservice); return rule; } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceConfiguration.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceConfiguration.java index 25af13b0f9c..d65726e0ada 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceConfiguration.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceConfiguration.java @@ -18,7 +18,12 @@ import java.util.List; -import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.loadbalance.filter.InstancePropertyDiscoveryFilter; +import org.apache.servicecomb.loadbalance.filter.PriorityInstancePropertyDiscoveryFilter; +import org.apache.servicecomb.loadbalance.filter.ServerDiscoveryFilter; +import org.apache.servicecomb.loadbalance.filter.ZoneAwareDiscoveryFilter; +import org.apache.servicecomb.registry.discovery.DiscoveryTree; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -32,17 +37,38 @@ public class LoadBalanceConfiguration { public static final String LOAD_BALANCE_ENABLED = LOAD_BALANCE_PREFIX + ".enabled"; @Bean - public ConsumerFilter loadBalanceFilter(ExtensionsManager extensionsManager) { - return new LoadBalanceFilter(extensionsManager); + public LoadBalanceFilter scbLoadBalanceFilter(ExtensionsManager extensionsManager, DiscoveryTree discoveryTree, + SCBEngine scbEngine) { + return new LoadBalanceFilter(extensionsManager, discoveryTree, scbEngine); } @Bean - public RuleNameExtentionsFactory ruleNameExtentionsFactory() { - return new RuleNameExtentionsFactory(); + public RuleNameExtensionsFactory scbRuleNameExtensionsFactory() { + return new RuleNameExtensionsFactory(); } @Bean - public ExtensionsManager extensionsManager(List extensionsFactories) { + public ExtensionsManager scbExtensionsManager(List extensionsFactories) { return new ExtensionsManager(extensionsFactories); } + + @Bean + public PriorityInstancePropertyDiscoveryFilter scbPriorityInstancePropertyDiscoveryFilter() { + return new PriorityInstancePropertyDiscoveryFilter(); + } + + @Bean + public InstancePropertyDiscoveryFilter scbInstancePropertyDiscoveryFilter() { + return new InstancePropertyDiscoveryFilter(); + } + + @Bean + public ServerDiscoveryFilter scbServerDiscoveryFilter() { + return new ServerDiscoveryFilter(); + } + + @Bean + public ZoneAwareDiscoveryFilter scbZoneAwareDiscoveryFilter() { + return new ZoneAwareDiscoveryFilter(); + } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceFilter.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceFilter.java index 06a1064a77a..e97263ac465 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceFilter.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceFilter.java @@ -18,38 +18,42 @@ import java.net.URI; import java.util.Map; -import java.util.Objects; +import java.util.Set; import java.util.concurrent.CompletableFuture; - -import javax.annotation.Nonnull; -import javax.ws.rs.core.Response.Status; +import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.governance.RetryContext; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; -import org.apache.servicecomb.loadbalance.filter.ServerDiscoveryFilter; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.loadbalance.Configuration.RuleType; import org.apache.servicecomb.registry.discovery.DiscoveryContext; -import org.apache.servicecomb.registry.discovery.DiscoveryFilter; import org.apache.servicecomb.registry.discovery.DiscoveryTree; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import com.google.common.annotations.VisibleForTesting; -import com.netflix.config.DynamicPropertyFactory; +import com.google.common.eventbus.Subscribe; + +import io.github.resilience4j.core.metrics.Metrics.Outcome; +import jakarta.ws.rs.core.Response.Status; -public class LoadBalanceFilter implements ConsumerFilter { +public class LoadBalanceFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { public static final String CONTEXT_KEY_LAST_SERVER = "x-context-last-server"; // Enough times to make sure to choose a different server in high volume. @@ -59,15 +63,9 @@ public class LoadBalanceFilter implements ConsumerFilter { public static final String SERVICECOMB_SERVER_ENDPOINT = "scb-endpoint"; - // set endpoint in invocation.localContext - // ignore logic of loadBalance - public static final boolean supportDefinedEndpoint = - DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.loadbalance.userDefinedEndpoint.enabled", false).get(); - private static final Logger LOGGER = LoggerFactory.getLogger(LoadBalanceFilter.class); - private DiscoveryTree discoveryTree = new DiscoveryTree(); + private final DiscoveryTree discoveryTree; // key为grouping filter qualified name private final Map loadBalancerMap = new ConcurrentHashMapEx<>(); @@ -76,34 +74,51 @@ public class LoadBalanceFilter implements ConsumerFilter { private final ExtensionsManager extensionsManager; - private String strategy = null; + private final SCBEngine scbEngine; - @VisibleForTesting - public LoadBalanceFilter(DiscoveryTree discoveryTree, ExtensionsManager extensionsManager) { - this.discoveryTree = discoveryTree; + // set endpoint in invocation.localContext + // ignore logic of loadBalance + public boolean supportDefinedEndpoint; + + @Autowired + public LoadBalanceFilter(ExtensionsManager extensionsManager, + DiscoveryTree discoveryTree, SCBEngine scbEngine) { + preCheck(scbEngine); + this.scbEngine = scbEngine; + this.supportDefinedEndpoint = this.scbEngine.getEnvironment() + .getProperty("servicecomb.loadbalance.userDefinedEndpoint.enabled", + boolean.class, false); this.extensionsManager = extensionsManager; + this.discoveryTree = discoveryTree; + EventManager.register(this); } - public LoadBalanceFilter(ExtensionsManager extensionsManager) { - preCheck(); - this.extensionsManager = extensionsManager; - discoveryTree.loadFromSPI(DiscoveryFilter.class); - discoveryTree.addFilter(new ServerDiscoveryFilter()); - discoveryTree.sort(); + @Subscribe + @SuppressWarnings("unused") + public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { + Set changedKeys = event.getChanged(); + for (String key : changedKeys) { + if (key.startsWith(Configuration.ROOT)) { + synchronized (lock) { + clearLoadBalancer(); + } + LOGGER.info("clear load balance rule for configuration changed, {}", key); + break; + } + } } - private void preCheck() { + private void preCheck(SCBEngine scbEngine) { // Old configurations check.Just print an error, because configurations may given in dynamic and fail on runtime. - - String policyName = DynamicPropertyFactory.getInstance() - .getStringProperty("servicecomb.loadbalance.NFLoadBalancerRuleClassName", null) - .get(); + String policyName = scbEngine.getEnvironment() + .getProperty("servicecomb.loadbalance.NFLoadBalancerRuleClassName"); if (!StringUtils.isEmpty(policyName)) { LOGGER.error("[servicecomb.loadbalance.NFLoadBalancerRuleClassName] is not supported anymore." + "use [servicecomb.loadbalance.strategy.name] instead."); } - String filterNames = Configuration.getStringProperty(null, "servicecomb.loadbalance.serverListFilters"); + String filterNames = scbEngine.getEnvironment() + .getProperty("servicecomb.loadbalance.serverListFilters"); if (!StringUtils.isEmpty(filterNames)) { LOGGER.error( "Server list implementation changed to SPI. Configuration [servicecomb.loadbalance.serverListFilters]" + @@ -113,11 +128,10 @@ private void preCheck() { } @Override - public int getOrder(InvocationType invocationType, String microservice) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER; } - @Nonnull @Override public String getName() { return "load-balance"; @@ -136,23 +150,17 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne invocation.addLocalContext(RetryContext.RETRY_LOAD_BALANCE, true); - String strategy = Configuration.INSTANCE.getRuleStrategyName(invocation.getMicroserviceName()); - if (!Objects.equals(strategy, this.strategy)) { - //配置变化,需要重新生成所有的lb实例 - synchronized (lock) { - clearLoadBalancer(); - } - } - this.strategy = strategy; - LoadBalancer loadBalancer = getOrCreateLoadBalancer(invocation); return send(invocation, nextNode, loadBalancer); } // user's can invoke a service by supplying target Endpoint. - // in this case, we do not using load balancer, and no stats of server calculated, no retrying. + // in this case, do not use load balancer, and no stats of server calculated, no retrying. private boolean handleSuppliedEndpoint(Invocation invocation) throws Exception { + if (invocation.isEdge()) { + return false; + } if (invocation.getEndpoint() != null) { return true; } @@ -166,7 +174,7 @@ private boolean handleSuppliedEndpoint(Invocation invocation) throws Exception { private Endpoint parseEndpoint(String endpointUri) throws Exception { URI formatUri = new URI(endpointUri); - Transport transport = SCBEngine.getInstance().getTransportManager().findTransport(formatUri.getScheme()); + Transport transport = scbEngine.getTransportManager().findTransport(formatUri.getScheme()); if (transport == null) { LOGGER.error("not deployed transport {}, ignore {}.", formatUri.getScheme(), endpointUri); throw new InvocationException(Status.BAD_REQUEST, @@ -199,18 +207,16 @@ CompletableFuture send(Invocation invocation, FilterNode filterNode, L ServiceCombServer server = chooseServer(invocation, chosenLB); if (null == server) { return CompletableFuture.failedFuture( - new InvocationException(Status.INTERNAL_SERVER_ERROR, "No available address found.")); + new InvocationException(Status.INTERNAL_SERVER_ERROR, + String.format("No available address found for %s/%s.", invocation.getAppId(), + invocation.getMicroserviceName()))); } - chosenLB.getLoadBalancerStats().incrementNumRequests(server); invocation.setEndpoint(server.getEndpoint()); return filterNode.onFilter(invocation).whenComplete((r, e) -> { - // The stats are for WeightedResponseTimeRule - chosenLB.getLoadBalancerStats().noteResponseTime(server, (System.currentTimeMillis() - time)); if (e != null || isFailedResponse(r)) { - // The stats are for SessionStickinessRule - chosenLB.getLoadBalancerStats().incrementSuccessiveConnectionFailureCount(server); + server.getServerMetrics().record((System.currentTimeMillis() - time), TimeUnit.MILLISECONDS, Outcome.ERROR); } else { - chosenLB.getLoadBalancerStats().incrementActiveRequestsCount(server); + server.getServerMetrics().record((System.currentTimeMillis() - time), TimeUnit.MILLISECONDS, Outcome.SUCCESS); } }); } @@ -242,10 +248,10 @@ private ServiceCombServer chooseServer(Invocation invocation, LoadBalancer chose } } - LOGGER.info("operation failed {}, retry to instance [{}], last instance [{}], trace id {}", + LOGGER.warn("operation failed {}, retry to instance [{}], last instance [{}], trace id {}", invocation.getMicroserviceQualifiedName(), - nextServer == null ? "" : nextServer.getHostPort(), - lastServer == null ? "" : lastServer.getHostPort(), + nextServer == null ? "" : nextServer.getEndpoint().getEndpoint(), + lastServer == null ? "" : nextServer.getEndpoint().getEndpoint(), invocation.getTraceId()); invocation.addLocalContext(CONTEXT_KEY_LAST_SERVER, nextServer); return nextServer; @@ -271,16 +277,25 @@ protected LoadBalancer getOrCreateLoadBalancer(Invocation invocation) { context.setInputParameters(invocation); VersionedCache serversVersionedCache = discoveryTree.discovery(context, invocation.getAppId(), - invocation.getMicroserviceName(), - invocation.getMicroserviceVersionRule()); + invocation.getMicroserviceName()); invocation.addLocalContext(CONTEXT_KEY_SERVER_LIST, serversVersionedCache.data()); - return loadBalancerMap - .computeIfAbsent(serversVersionedCache.name(), name -> createLoadBalancer(invocation.getMicroserviceName())); + RuleType ruleType = Configuration.INSTANCE.getRuleStrategyName(invocation); + String cacheKey; + if (ruleType.getType() == RuleType.TYPE_SCHEMA) { + cacheKey = invocation.getAppId() + "-" + + invocation.getMicroserviceName() + "-" + invocation.getSchemaId(); + } else { + cacheKey = invocation.getAppId() + "-" + + invocation.getMicroserviceName() + "-" + invocation.getSchemaId() + "-" + invocation.getOperationName(); + } + return loadBalancerMap.computeIfAbsent(cacheKey, + key -> createLoadBalancer(ruleType, key, invocation.getMicroserviceName())); } - private LoadBalancer createLoadBalancer(String microserviceName) { - RuleExt rule = extensionsManager.createLoadBalancerRule(microserviceName); + private LoadBalancer createLoadBalancer(RuleType ruleType, String cacheKey, String microserviceName) { + RuleExt rule = extensionsManager.createLoadBalancerRule(ruleType.getValue()); + LOGGER.info("Using load balance rule {} for microservice {}.", rule.getClass().getName(), cacheKey); return new LoadBalancer(rule, microserviceName); } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalancer.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalancer.java index 74ce50153d1..d60581ff5f0 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalancer.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalancer.java @@ -26,7 +26,6 @@ import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; -import com.netflix.loadbalancer.LoadBalancerStats; /** * A load balancer with RuleExt and ServerListFilterExt @@ -38,8 +37,6 @@ public class LoadBalancer { private final RuleExt rule; - private final LoadBalancerStats lbStats; - private final String microServiceName; private List filters; @@ -47,7 +44,6 @@ public class LoadBalancer { public LoadBalancer(RuleExt rule, String microServiceName) { this.microServiceName = microServiceName; this.rule = rule; - this.lbStats = new LoadBalancerStats(microServiceName + id.getAndDecrement()); // load new instances, because filters work on service information this.filters = SPIServiceUtils.loadSortedService(ServerListFilterExt.class); this.rule.setLoadBalancer(this); @@ -74,10 +70,6 @@ public ServiceCombServer chooseServer(Invocation invocation) { return server; } - public LoadBalancerStats getLoadBalancerStats() { - return lbStats; - } - public String getMicroServiceName() { return microServiceName; } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadbalanceClientConfig.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadbalanceClientConfig.java deleted file mode 100644 index ce55008ca34..00000000000 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadbalanceClientConfig.java +++ /dev/null @@ -1,33 +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.servicecomb.loadbalance; - -import com.netflix.client.config.DefaultClientConfigImpl; - -/** - * 配置转换(暂时没有配置项,待新增) - * - */ -public class LoadbalanceClientConfig extends DefaultClientConfigImpl { - private static final String PREFIX = "servicecomb.loadbalance"; - - public LoadbalanceClientConfig(String namespace) { - super(namespace); - loadProperties(PREFIX); - } -} diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RandomRuleExt.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RandomRuleExt.java index 5f8d26c0ff8..318b45f3839 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RandomRuleExt.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RandomRuleExt.java @@ -31,7 +31,7 @@ public ServiceCombServer choose(List servers, Invocation invo if (servers.isEmpty()) { return null; } - int index = Math.abs(ThreadLocalRandom.current().nextInt()) % servers.size(); + int index = ThreadLocalRandom.current().nextInt(servers.size()); return servers.get(index); } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleNameExtensionsFactory.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleNameExtensionsFactory.java new file mode 100644 index 00000000000..9d9b0e0da1b --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleNameExtensionsFactory.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.loadbalance; + +import java.util.Collection; + +import com.google.common.collect.Lists; + +public class RuleNameExtensionsFactory implements ExtensionsFactory { + private static final Collection ACCEPT_KEYS = Lists.newArrayList( + Configuration.RULE_STRATEGY_NAME); + + private static final String RULE_RoundRobin = "RoundRobin"; + + private static final String RULE_Random = "Random"; + + private static final String RULE_WeightedResponse = "WeightedResponse"; + + private static final String RULE_SessionStickiness = "SessionStickiness"; + + private static final Collection ACCEPT_VALUES = Lists.newArrayList( + RULE_RoundRobin, + RULE_Random, + RULE_WeightedResponse, + RULE_SessionStickiness); + + @Override + public boolean isSupport(String key, String value) { + return ACCEPT_KEYS.contains(key) && ACCEPT_VALUES.contains(value); + } + + @Override + public RuleExt createLoadBalancerRule(String ruleName) { + if (RULE_RoundRobin.equals(ruleName)) { + return new RoundRobinRuleExt(); + } else if (RULE_Random.equals(ruleName)) { + return new RandomRuleExt(); + } else if (RULE_WeightedResponse.equals(ruleName)) { + return new WeightedResponseTimeRuleExt(); + } else if (RULE_SessionStickiness.equals(ruleName)) { + return new SessionStickinessRule(); + } else { + throw new IllegalStateException("unexpected code to reach here, value is " + ruleName); + } + } +} diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleNameExtentionsFactory.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleNameExtentionsFactory.java deleted file mode 100644 index 9315928eae4..00000000000 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleNameExtentionsFactory.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.servicecomb.loadbalance; - -import java.util.Collection; - -import com.google.common.collect.Lists; - -public class RuleNameExtentionsFactory implements ExtensionsFactory { - private static final Collection ACCEPT_KEYS = Lists.newArrayList( - Configuration.RULE_STRATEGY_NAME); - - private static final String RULE_RoundRobin = "RoundRobin"; - - private static final String RULE_Random = "Random"; - - private static final String RULE_WeightedResponse = "WeightedResponse"; - - private static final String RULE_SessionStickiness = "SessionStickiness"; - - private static final Collection ACCEPT_VALUES = Lists.newArrayList( - RULE_RoundRobin, - RULE_Random, - RULE_WeightedResponse, - RULE_SessionStickiness); - - @Override - public boolean isSupport(String key, String value) { - return ACCEPT_KEYS.contains(key) && ACCEPT_VALUES.contains(value); - } - - @Override - public RuleExt createLoadBalancerRule(String ruleName) { - if (RULE_RoundRobin.equals(ruleName)) { - return new RoundRobinRuleExt(); - } else if (RULE_Random.equals(ruleName)) { - return new RandomRuleExt(); - } else if (RULE_WeightedResponse.equals(ruleName)) { - return new WeightedResponseTimeRuleExt(); - } else if (RULE_SessionStickiness.equals(ruleName)) { - return new SessionStickinessRule(); - } else { - throw new IllegalStateException("unexpected code to reach here, value is " + ruleName); - } - } -} diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServerListFilterExt.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServerListFilterExt.java index 002bb3a92a7..9d2d07ceb5f 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServerListFilterExt.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServerListFilterExt.java @@ -30,16 +30,6 @@ public interface ServerListFilterExt { int ORDER_NORMAL = 0; - int ORDER_ISOLATION = -100; - - int ORDER_ZONE_AWARE = 200; - - String EMPTY_INSTANCE_PROTECTION = "servicecomb.loadbalance.filter.isolation.emptyInstanceProtectionEnabled"; - - String ISOLATION_FILTER_ENABLED = "servicecomb.loadbalance.filter.isolation.enabled"; - - String ZONE_AWARE_FILTER_ENABLED = "servicecomb.loadbalance.filter.zoneaware.enabled"; - default int getOrder() { return ORDER_NORMAL; } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServerMetrics.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServerMetrics.java new file mode 100644 index 00000000000..d756ff40300 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServerMetrics.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.servicecomb.loadbalance; + +import java.util.concurrent.TimeUnit; + +import io.github.resilience4j.core.metrics.FixedSizeSlidingWindowMetrics; +import io.github.resilience4j.core.metrics.Metrics.Outcome; +import io.github.resilience4j.core.metrics.Snapshot; + +/** + * ServiceCombServer states + */ +public class ServerMetrics { + private final FixedSizeSlidingWindowMetrics metrics = new FixedSizeSlidingWindowMetrics(50); + + public Snapshot record(long duration, TimeUnit durationUnit, Outcome outcome) { + return metrics.record(duration, durationUnit, outcome); + } + + public Snapshot getSnapshot() { + return metrics.getSnapshot(); + } +} diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombServer.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombServer.java index bcf255f1a27..edfdf322f6e 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombServer.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombServer.java @@ -17,66 +17,27 @@ package org.apache.servicecomb.loadbalance; -import java.net.URI; -import java.net.URISyntaxException; +import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.Endpoint; -import org.apache.servicecomb.core.Transport; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.cache.CacheEndpoint; - -import com.google.common.annotations.VisibleForTesting; -import com.netflix.loadbalancer.Server; +import org.apache.servicecomb.registry.api.DiscoveryInstance; /** - * 服务器抽象,address只有transport识别, LB模块不识别 - * LB模块不提供服务器状态监测,这块功能是由注册中心进行处理的。 + * Server object used for transports and load balancer. * */ -public class ServiceCombServer extends Server { +public class ServiceCombServer { private final Endpoint endpoint; - // 所属服务实例 - private final MicroserviceInstance instance; - private final String microserviceName; - @VisibleForTesting - ServiceCombServer(String microserviceName, Endpoint endpoint, MicroserviceInstance instance) { - super(null); - this.endpoint = endpoint; - this.instance = instance; - this.microserviceName = microserviceName; - - // Different types of Robin Component Rule have different usages for server status and list. - // e.g. RoundRobinRule using getAllServers & alive & readyToServe - // RandomRule using getReachableServers & alive - // WeightedResponseTimeRule using getAllServers & alive - // To make all rules work only on "how to choose a server from alive servers", we do not rely on Robbin defined status - this.setAlive(true); - this.setReadyToServe(true); - } + private final ServerMetrics serverMetrics; - public ServiceCombServer(String microserviceName, Transport transport, CacheEndpoint cacheEndpoint) { - super(null); + public ServiceCombServer(String microserviceName, Endpoint endpoint) { this.microserviceName = microserviceName; - endpoint = new Endpoint(transport, cacheEndpoint.getEndpoint(), cacheEndpoint.getInstance()); - instance = cacheEndpoint.getInstance(); - - // Different types of Robin Component Rule have different usages for server status and list. - // e.g. RoundRobinRule using getAllServers & alive & readyToServe - // RandomRule using getReachableServers & alive - // WeightedResponseTimeRule using getAllServers & alive - // To make all rules work only on "how to choose a server from alive servers", we do not rely on Robbin defined status - this.setAlive(true); - this.setReadyToServe(true); - try { - URI endpointURI = new URI(endpoint.getEndpoint()); - setHost(endpointURI.getHost()); - setPort(endpointURI.getPort()); - } catch (URISyntaxException ignored) { - } + this.endpoint = endpoint; + this.serverMetrics = new ServerMetrics(); } public String getMicroserviceName() { @@ -87,8 +48,8 @@ public Endpoint getEndpoint() { return endpoint; } - public MicroserviceInstance getInstance() { - return instance; + public DiscoveryInstance getInstance() { + return endpoint.getMicroserviceInstance(); } @Override @@ -96,16 +57,19 @@ public String toString() { return endpoint.getEndpoint(); } - // used in LoadBalancerContext - @Override public String getHost() { return endpoint.getEndpoint(); } + public ServerMetrics getServerMetrics() { + return serverMetrics; + } + @Override public boolean equals(Object o) { if (o instanceof ServiceCombServer) { - return this.instance.getInstanceId().equals(((ServiceCombServer) o).instance.getInstanceId()) + return this.getInstance().getInstanceId() + .equals(((ServiceCombServer) o).getInstance().getInstanceId()) && StringUtils.equals(endpoint.getEndpoint(), ((ServiceCombServer) o).getEndpoint().getEndpoint()); } else { return false; @@ -114,6 +78,6 @@ public boolean equals(Object o) { @Override public int hashCode() { - return this.instance.getInstanceId().hashCode(); + return Objects.hash(this.getInstance().getInstanceId(), this.endpoint); } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/SessionStickinessRule.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/SessionStickinessRule.java index 7483d9c9b9e..37d94bc0fa1 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/SessionStickinessRule.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/SessionStickinessRule.java @@ -19,13 +19,11 @@ import java.util.List; -import com.google.common.annotations.VisibleForTesting; import org.apache.servicecomb.core.Invocation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.netflix.loadbalancer.LoadBalancerStats; -import com.netflix.loadbalancer.ServerStats; +import com.google.common.annotations.VisibleForTesting; /** * 会话保持策略:优先选择上一次选中的服务器,保证请求都发送到同一个服务器上去。 @@ -37,14 +35,12 @@ public class SessionStickinessRule implements RuleExt { private final Object lock = new Object(); - private LoadBalancer loadBalancer; - // use random rule as the trigger rule, to prevent consumer instance select the same producer instance. private final RuleExt triggerRule; private volatile ServiceCombServer lastServer = null; - private long lastAccessedTime = 0; + private volatile long lastAccessedTime = 0; private volatile boolean errorThresholdMet = false; @@ -56,9 +52,9 @@ public SessionStickinessRule() { triggerRule = new RoundRobinRuleExt(); } + @Override public void setLoadBalancer(LoadBalancer loadBalancer) { this.microserviceName = loadBalancer.getMicroServiceName(); - this.loadBalancer = loadBalancer; } private ServiceCombServer chooseNextServer(List servers, Invocation invocation) { @@ -103,19 +99,10 @@ private boolean isTimeOut() { * MILLI_COUNT_IN_SECOND); } - private boolean isErrorThresholdMet() { - LoadBalancerStats stats = loadBalancer.getLoadBalancerStats(); - - if (stats != null && stats.getServerStats() != null && stats.getServerStats().size() > 0) { - ServerStats serverStats = stats.getSingleServerStat(lastServer); - int successiveFailedCount = serverStats.getSuccessiveConnectionFailureCount(); - if (Configuration.INSTANCE.getSuccessiveFailedTimes(microserviceName) > 0 - && successiveFailedCount >= Configuration.INSTANCE.getSuccessiveFailedTimes(microserviceName)) { - serverStats.clearSuccessiveConnectionFailureCount(); - return true; - } - } - return false; + private boolean isErrorThresholdMet(ServiceCombServer server) { + int successiveFailedCount = server.getServerMetrics().getSnapshot().getNumberOfFailedCalls(); + return Configuration.INSTANCE.getSuccessiveFailedTimes(microserviceName) > 0 + && successiveFailedCount >= Configuration.INSTANCE.getSuccessiveFailedTimes(microserviceName); } @Override @@ -131,7 +118,7 @@ public ServiceCombServer choose(List servers, Invocation invo this.lastAccessedTime = System.currentTimeMillis(); } - if (isErrorThresholdMet()) { + if (isErrorThresholdMet(lastServer)) { LOG.warn("reached max error. choose another server."); errorThresholdMet = true; return chooseServerErrorThresholdMet(servers, invocation); diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/WeightedResponseTimeRuleExt.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/WeightedResponseTimeRuleExt.java index 7a6fe6c7310..a6462876e93 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/WeightedResponseTimeRuleExt.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/WeightedResponseTimeRuleExt.java @@ -20,33 +20,40 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.Invocation; -import com.netflix.loadbalancer.ServerStats; - /** * Rule based on response time. */ public class WeightedResponseTimeRuleExt extends RoundRobinRuleExt { - // 10ms + // when all servers are very fast(less than MIN_GAP), use round-robin rule. private static final double MIN_GAP = 10d; - private static final int RANDOM_PERCENT = 10; + // calculate stats once per RANDOM_PERCENT requests. + private static final int RANDOM_PERCENT = 1000; - private LoadBalancer loadBalancer; + private final Object lock = new Object(); - private double totalWeightsCache = -1d; + private final AtomicInteger counter = new AtomicInteger(0); - @Override - public void setLoadBalancer(LoadBalancer loadBalancer) { - this.loadBalancer = loadBalancer; - } + // notices: rule will always use as a fixed group of instance, see LoadBalancer for details. + private volatile int size = -1; + + private volatile List cacheStates = new ArrayList<>(); @Override public ServiceCombServer choose(List servers, Invocation invocation) { - List stats = calculateTotalWeights(servers); + int count = counter.getAndIncrement(); + if (count % RANDOM_PERCENT == 0 || size != servers.size()) { + synchronized (lock) { + this.cacheStates = doCalculateTotalWeights(servers); + this.size = servers.size(); + } + } + List stats = this.cacheStates; if (stats.size() > 0) { double finalTotal = stats.get(stats.size() - 1); List weights = new ArrayList<>(servers.size()); @@ -65,26 +72,13 @@ public ServiceCombServer choose(List servers, Invocation invo return super.choose(servers, invocation); } - private List calculateTotalWeights(List servers) { - if (Double.compare(totalWeightsCache, 0) < 0 || Double.compare(totalWeightsCache, MIN_GAP * servers.size()) > 0) { - return doCalculateTotalWeights(servers); - } - // 10% possibilities to use weighed response rule when the normal access is very fast. - if (ThreadLocalRandom.current().nextInt(RANDOM_PERCENT) == 0) { - return doCalculateTotalWeights(servers); - } else { - return new ArrayList<>(); - } - } - - private List doCalculateTotalWeights(List servers) { + private static List doCalculateTotalWeights(List servers) { List stats = new ArrayList<>(servers.size() + 1); double totalWeights = 0; boolean needRandom = false; for (ServiceCombServer server : servers) { - ServerStats serverStats = loadBalancer.getLoadBalancerStats().getSingleServerStat(server); - //getResponseTimeAvgRecent()按照时间窗口统计,时间窗口大小为1分钟;getResponseTimeAvg()一直累积 - double avgTime = serverStats.getResponseTimeAvgRecent(); + // this method will create new instance, so we cache the states. + double avgTime = server.getServerMetrics().getSnapshot().getAverageDuration().toMillis(); if (!needRandom && avgTime > MIN_GAP) { needRandom = true; } @@ -92,7 +86,6 @@ private List doCalculateTotalWeights(List servers) { stats.add(avgTime); } stats.add(totalWeights); - totalWeightsCache = totalWeights; if (needRandom) { return stats; } else { diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/InstancePropertyDiscoveryFilter.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/InstancePropertyDiscoveryFilter.java index 6171ac54468..2b4c3eb6b20 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/InstancePropertyDiscoveryFilter.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/InstancePropertyDiscoveryFilter.java @@ -17,25 +17,27 @@ package org.apache.servicecomb.loadbalance.filter; -import java.util.HashMap; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.loadbalance.Configuration; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; import org.apache.servicecomb.registry.discovery.AbstractDiscoveryFilter; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; - -import com.netflix.config.DynamicPropertyFactory; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; /** - * Instance property based filter + * Instance property based filter */ public class InstancePropertyDiscoveryFilter extends AbstractDiscoveryFilter { + private static final String MATCHED = "matched"; + public static final String SERVICECOMB_LOADBALANCE_FILTER_INSTANCE_PROPERTY_ENABLED = "servicecomb.loadbalance.filter.instanceProperty.enabled"; + @Override public int getOrder() { return 400; @@ -43,8 +45,13 @@ public int getOrder() { @Override public boolean enabled() { - return DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.loadbalance.filter.instanceProperty.enabled", true).get(); + + if (enabled == null) { + enabled = dynamicProperties.getBooleanProperty(SERVICECOMB_LOADBALANCE_FILTER_INSTANCE_PROPERTY_ENABLED, + value -> enabled = value, + true); + } + return enabled; } @Override @@ -54,14 +61,14 @@ public boolean isGroupingFilter() { @Override protected void init(DiscoveryContext context, DiscoveryTreeNode parent) { - Map matchedInstance = new HashMap<>(); + List matchedInstance = new ArrayList<>(); Invocation invocation = context.getInputParameters(); - Map instances = parent.data(); + List instances = parent.data(); Map filterOptions = Configuration.INSTANCE.getFlowsplitFilterOptions(invocation.getMicroserviceName()); - instances.forEach((key, target) -> { + instances.forEach((target) -> { if (allowVisit(target, filterOptions)) { - matchedInstance.put(key, target); + matchedInstance.add(target); } }); parent.child(MATCHED, new DiscoveryTreeNode() @@ -74,7 +81,7 @@ protected String findChildName(DiscoveryContext context, DiscoveryTreeNode paren return MATCHED; } - protected boolean allowVisit(MicroserviceInstance instance, Map filterOptions) { + protected boolean allowVisit(StatefulDiscoveryInstance instance, Map filterOptions) { Map propertiesMap = instance.getProperties(); for (Entry entry : filterOptions.entrySet()) { if (!entry.getValue().equals(propertiesMap.get(entry.getKey()))) { diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilter.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilter.java index ea98ca7376d..d890b7ea229 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilter.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilter.java @@ -17,24 +17,26 @@ package org.apache.servicecomb.loadbalance.filter; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import javax.validation.constraints.NotNull; - import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; import org.apache.servicecomb.registry.discovery.AbstractDiscoveryFilter; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; -import com.netflix.config.DynamicPropertyFactory; +import jakarta.validation.constraints.NotNull; /** * Instance property with priority filter @@ -45,24 +47,34 @@ public class PriorityInstancePropertyDiscoveryFilter extends AbstractDiscoveryFi private static final String ALL_INSTANCE = "allInstance"; + public static final String SERVICECOMB_LOADBALANCE_FILTER_PRIORITY_INSTANCE_PROPERTY_KEY = "servicecomb.loadbalance.filter.priorityInstanceProperty.key"; + private String propertyKey; + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + @Override protected void init(DiscoveryContext context, DiscoveryTreeNode parent) { - propertyKey = DynamicPropertyFactory.getInstance() - .getStringProperty("servicecomb.loadbalance.filter.priorityInstanceProperty.key", "environment") - .get(); + + propertyKey = dynamicProperties.getStringProperty(SERVICECOMB_LOADBALANCE_FILTER_PRIORITY_INSTANCE_PROPERTY_KEY, + value -> propertyKey = value, + "environment"); // group all instance by property - Map instances = parent.data(); - Map> groupByProperty = new HashMap<>(); - for (MicroserviceInstance microserviceInstance : instances.values()) { + List instances = parent.data(); + Map> groupByProperty = new HashMap<>(); + for (StatefulDiscoveryInstance microserviceInstance : instances) { String propertyValue = new PriorityInstanceProperty(propertyKey, microserviceInstance).getPropertyValue(); - groupByProperty.computeIfAbsent(propertyValue, key -> new HashMap<>()) - .put(microserviceInstance.getInstanceId(), microserviceInstance); + groupByProperty.computeIfAbsent(propertyValue, key -> new ArrayList<>()) + .add(microserviceInstance); } Map children = new HashMap<>(); - for (Map.Entry> entry : groupByProperty.entrySet()) { + for (Map.Entry> entry : groupByProperty.entrySet()) { children.put(entry.getKey(), new DiscoveryTreeNode().subName(parent, entry.getKey()).data(entry.getValue())); } @@ -77,7 +89,8 @@ protected String findChildName(DiscoveryContext context, DiscoveryTreeNode paren // context property has precedence over instance property String initPropertyValue = invocation.getContext() .computeIfAbsent("x-" + propertyKey, - key -> new PriorityInstanceProperty(propertyKey, RegistrationManager.INSTANCE.getMicroserviceInstance()) + key -> new PriorityInstanceProperty(propertyKey, + BootStrapProperties.readServiceProperties(environment).get(propertyKey)) .getPropertyValue()); PriorityInstanceProperty currentProperty = context.getContextParameter(propertyKey); @@ -106,9 +119,13 @@ protected String findChildName(DiscoveryContext context, DiscoveryTreeNode paren @Override public boolean enabled() { - return DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.loadbalance.filter.priorityInstanceProperty.enabled", false) - .get(); + + if (enabled == null) { + enabled = dynamicProperties.getBooleanProperty("servicecomb.loadbalance.filter.priorityInstanceProperty.enabled", + value -> enabled = value, + false); + } + return enabled; } @Override @@ -128,7 +145,7 @@ static class PriorityInstanceProperty { /** * Constructor * - * @param key property key + * @param key property key * @param value property value */ public PriorityInstanceProperty(@NotNull String key, String value) { @@ -145,11 +162,12 @@ public PriorityInstanceProperty(@NotNull String key, String value) { /** * Constructor * - * @param key property key + * @param key property key * @param microserviceInstance instance */ - public PriorityInstanceProperty(@NotNull String key, @NotNull MicroserviceInstance microserviceInstance) { - this(key, Optional.ofNullable(microserviceInstance.getProperties().get(key)).orElse(StringUtils.EMPTY)); + public PriorityInstanceProperty(@NotNull String key, @NotNull StatefulDiscoveryInstance microserviceInstance) { + this(key, Optional.ofNullable(microserviceInstance.getProperties().get(key)) + .orElse(StringUtils.EMPTY)); } /** diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ServerDiscoveryFilter.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ServerDiscoveryFilter.java index 9e6b22eaef4..50baab772fb 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ServerDiscoveryFilter.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ServerDiscoveryFilter.java @@ -17,29 +17,32 @@ package org.apache.servicecomb.loadbalance.filter; +import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.registry.discovery.EndpointDiscoveryFilter; import org.apache.servicecomb.loadbalance.ServiceCombServer; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.cache.CacheEndpoint; import org.apache.servicecomb.registry.discovery.DiscoveryContext; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ServerDiscoveryFilter extends EndpointDiscoveryFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ServerDiscoveryFilter.class); + public ServerDiscoveryFilter() { + } + @Override protected Object createEndpoint(DiscoveryContext context, String transportName, String endpoint, - MicroserviceInstance instance) { - Transport transport = SCBEngine.getInstance().getTransportManager().findTransport(transportName); + StatefulDiscoveryInstance instance) { + Transport transport = scbEngine.getTransportManager().findTransport(transportName); if (transport == null) { LOGGER.info("not deployed transport {}, ignore {}.", transportName, endpoint); return null; } Invocation invocation = context.getInputParameters(); - return new ServiceCombServer(invocation.getMicroserviceName(), transport, new CacheEndpoint(endpoint, instance)); + return new ServiceCombServer(invocation.getMicroserviceName(), + new Endpoint(transport, endpoint, instance)); } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ZoneAwareDiscoveryFilter.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ZoneAwareDiscoveryFilter.java new file mode 100644 index 00000000000..0e521397f13 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ZoneAwareDiscoveryFilter.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.servicecomb.loadbalance.filter; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.registry.discovery.AbstractGroupDiscoveryFilter; +import org.apache.servicecomb.registry.discovery.DiscoveryContext; +import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; +import org.springframework.beans.factory.annotation.Autowired; + +public class ZoneAwareDiscoveryFilter extends AbstractGroupDiscoveryFilter { + public static final String PARAMETER = "zone_aware_level"; + + public static final String GROUP_PREFIX = "zone_aware_group_"; + + public static final String GROUP_SIZE = "zone_aware_group_size"; + + public static final String CONFIG_ENABLED = "servicecomb.loadbalance.filter.zoneaware.enabled"; + + public static final String CONFIG_RATIO = "servicecomb.loadbalance.filter.zoneaware.ratio"; + + public static final String CONFIG_RATIO_CEILING = "servicecomb.loadbalance.filter.zoneaware.ratioCeiling"; + + private DataCenterProperties dataCenterProperties; + + private Integer ratio; + + private Integer ratioCeiling; + + + @Autowired + @SuppressWarnings("unused") + public void setDataCenterProperties(DataCenterProperties dataCenterProperties) { + this.dataCenterProperties = dataCenterProperties; + } + + @Override + public int getOrder() { + return -9000; + } + + @Override + public boolean enabled() { + + if (enabled == null) { + enabled = dynamicProperties.getBooleanProperty(CONFIG_ENABLED, + value -> enabled = value, + true); + } + return enabled; + } + + private int getRatio() { + + if (ratio == null) { + ratio = dynamicProperties.getIntProperty(CONFIG_RATIO, + value -> ratio = value, + 30); + } + return ratio; + } + + private int getRatioCeiling(int defaultValue) { + + if (ratioCeiling == null) { + ratioCeiling = dynamicProperties.getIntProperty(CONFIG_RATIO_CEILING, + value -> ratioCeiling = value, + defaultValue); + } + return ratioCeiling; + } + + @Override + protected String contextParameter() { + return PARAMETER; + } + + @Override + protected String groupsSizeParameter() { + return GROUP_SIZE; + } + + @Override + protected String groupPrefix() { + return GROUP_PREFIX; + } + + @Override + public void init(DiscoveryContext context, DiscoveryTreeNode parent) { + List instances = parent.data(); + List instancesRegionAndAZMatch = new ArrayList<>(); + List instancesAZMatch = new ArrayList<>(); + List instancesNoMatch = new ArrayList<>(); + + int groups = 1; + + for (StatefulDiscoveryInstance server : instances) { + if (regionAndAZMatch(server)) { + instancesRegionAndAZMatch.add(server); + } else if (regionMatch(server)) { + instancesAZMatch.add(server); + } else { + instancesNoMatch.add(server); + } + } + + int ratio = getRatio(); + int ratioCeiling = getRatioCeiling(100 - ratio); + + if (hasEnoughMembers(instances.size(), instancesRegionAndAZMatch.size(), ratio, ratioCeiling)) { + parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() + .subName(parent, GROUP_PREFIX + groups).data(instancesRegionAndAZMatch)); + groups++; + } else { + instancesAZMatch.addAll(instancesRegionAndAZMatch); + } + + if (hasEnoughMembers(instances.size(), instancesAZMatch.size(), ratio, ratioCeiling)) { + parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() + .subName(parent, GROUP_PREFIX + groups).data(instancesAZMatch)); + groups++; + } else { + instancesNoMatch.addAll(instancesAZMatch); + } + + parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() + .subName(parent, GROUP_PREFIX + groups).data(instancesNoMatch)); + + parent.attribute(GROUP_SIZE, groups); + } + + private boolean hasEnoughMembers(int totalSize, int groupSize, int ratio, int ratioCeiling) { + if (totalSize == 0 || groupSize == 0) { + return false; + } + int actual = Math.floorDiv(groupSize * 100, totalSize); + return actual >= ratio && actual <= ratioCeiling; + } + + private boolean regionAndAZMatch(StatefulDiscoveryInstance target) { + if (dataCenterProperties.getRegion() != null + && dataCenterProperties.getAvailableZone() != null && target.getDataCenterInfo() != null) { + return dataCenterProperties.getRegion() + .equals(target.getDataCenterInfo().getRegion()) && + dataCenterProperties.getAvailableZone() + .equals(target.getDataCenterInfo().getAvailableZone()); + } + return false; + } + + private boolean regionMatch(StatefulDiscoveryInstance target) { + if (dataCenterProperties.getRegion() != null && target.getDataCenterInfo() != null) { + return dataCenterProperties.getRegion() + .equals(target.getDataCenterInfo().getRegion()); + } + return false; + } +} diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filterext/ZoneAwareDiscoveryFilter.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filterext/ZoneAwareDiscoveryFilter.java deleted file mode 100644 index d31597b5eb0..00000000000 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filterext/ZoneAwareDiscoveryFilter.java +++ /dev/null @@ -1,88 +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.servicecomb.loadbalance.filterext; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.loadbalance.ServerListFilterExt; -import org.apache.servicecomb.loadbalance.ServiceCombServer; -import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; - -import com.netflix.config.DynamicPropertyFactory; - -public class ZoneAwareDiscoveryFilter implements ServerListFilterExt { - - @Override - public int getOrder() { - return ORDER_ZONE_AWARE; - } - - @Override - public boolean enabled() { - return DynamicPropertyFactory.getInstance() - .getBooleanProperty(ZONE_AWARE_FILTER_ENABLED, true) - .get(); - } - - @Override - public List getFilteredListOfServers(List servers, - Invocation invocation) { - MicroserviceInstance myself = RegistrationManager.INSTANCE.getMicroserviceInstance(); - - List instancesRegionAndAZMatch = new ArrayList<>(); - List instancesAZMatch = new ArrayList<>(); - List instancesNoMatch = new ArrayList<>(); - servers.forEach((server) -> { - if (regionAndAZMatch(myself, server.getInstance())) { - instancesRegionAndAZMatch.add(server); - } else if (regionMatch(myself, server.getInstance())) { - instancesAZMatch.add(server); - } else { - instancesNoMatch.add(server); - } - }); - if (!instancesRegionAndAZMatch.isEmpty()) { - return instancesRegionAndAZMatch; - } else if (!instancesAZMatch.isEmpty()) { - return instancesAZMatch; - } - return instancesNoMatch; - } - - private boolean regionAndAZMatch(MicroserviceInstance myself, MicroserviceInstance target) { - if (myself == null || myself.getDataCenterInfo() == null) { - // when instance have no datacenter info, it will match all other datacenters - return true; - } - if (target.getDataCenterInfo() != null) { - return myself.getDataCenterInfo().getRegion().equals(target.getDataCenterInfo().getRegion()) && - myself.getDataCenterInfo().getAvailableZone().equals(target.getDataCenterInfo().getAvailableZone()); - } - return false; - } - - private boolean regionMatch(MicroserviceInstance myself, MicroserviceInstance target) { - if (target.getDataCenterInfo() != null) { - return myself.getDataCenterInfo().getRegion().equals(target.getDataCenterInfo().getRegion()); - } - return false; - } -} diff --git a/handlers/handler-loadbalance/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt b/handlers/handler-loadbalance/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt deleted file mode 100644 index 741fd2c7c58..00000000000 --- a/handlers/handler-loadbalance/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.loadbalance.filterext.ZoneAwareDiscoveryFilter diff --git a/handlers/handler-loadbalance/src/main/resources/META-INF/services/org.apache.servicecomb.registry.discovery.DiscoveryFilter b/handlers/handler-loadbalance/src/main/resources/META-INF/services/org.apache.servicecomb.registry.discovery.DiscoveryFilter deleted file mode 100644 index 445ab0efd23..00000000000 --- a/handlers/handler-loadbalance/src/main/resources/META-INF/services/org.apache.servicecomb.registry.discovery.DiscoveryFilter +++ /dev/null @@ -1,19 +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. -# - -org.apache.servicecomb.loadbalance.filter.InstancePropertyDiscoveryFilter -org.apache.servicecomb.loadbalance.filter.PriorityInstancePropertyDiscoveryFilter \ No newline at end of file diff --git a/handlers/handler-loadbalance/src/main/resources/META-INF/spring.factories b/handlers/handler-loadbalance/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 8ace8d00029..00000000000 --- a/handlers/handler-loadbalance/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,19 +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. -## --------------------------------------------------------------------------- - -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.loadbalance.LoadBalanceConfiguration diff --git a/handlers/handler-loadbalance/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/handlers/handler-loadbalance/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..995d86d443a --- /dev/null +++ b/handlers/handler-loadbalance/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.loadbalance.LoadBalanceConfiguration diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestConfiguration.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestConfiguration.java index 1b51a7c426b..81f24cd8b60 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestConfiguration.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestConfiguration.java @@ -17,21 +17,27 @@ package org.apache.servicecomb.loadbalance; -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.After; +import org.junit.Before; import org.junit.Test; - -import com.netflix.config.ConcurrentCompositeConfiguration; import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; /** * */ public class TestConfiguration { + Environment environment = Mockito.mock(Environment.class); + + @Before + public void setUp() { + LegacyPropertyFactory.setEnvironment(environment); + } + @After public void after() { - ArchaiusUtils.resetConfig(); } @Test @@ -61,31 +67,10 @@ public void testGetSessionTimeoutInSeconds() { Assertions.assertNotNull(Configuration.INSTANCE.getSessionTimeoutInSeconds("test")); } - - /** - * The property key of timerIntervalInMilis changed from servicecomb.loadbalance.stats.timerIntervalInMilis - * to servicecomb.loadbalance.stats.timerIntervalInMillis, check the compatibility - */ - @Test - public void testGetTimerIntervalInMillis() { - System.setProperty(Configuration.TIMER_INTERVAL_IN_MILLIS, "100"); - ConcurrentCompositeConfiguration localConfiguration = ConfigUtil.createLocalConfig(); - Assertions.assertEquals("100", localConfiguration.getProperty(Configuration.TIMER_INTERVAL_IN_MILLIS)); - - System.clearProperty(Configuration.TIMER_INTERVAL_IN_MILLIS); - localConfiguration = ConfigUtil.createLocalConfig(); - Assertions.assertNull(localConfiguration.getProperty(Configuration.TIMER_INTERVAL_IN_MILLIS)); - - System.setProperty("servicecomb.loadbalance.stats.timerIntervalInMilis", "100"); - localConfiguration = ConfigUtil.createLocalConfig(); - Assertions.assertEquals("100", localConfiguration.getProperty(Configuration.TIMER_INTERVAL_IN_MILLIS)); - } - @Test public void testGetMaxSingleTestWindow() { Assertions.assertEquals(60000, Configuration.INSTANCE.getMaxSingleTestWindow()); - - ArchaiusUtils.setProperty("servicecomb.loadbalance.isolation.maxSingleTestWindow", 5000); + Mockito.when(environment.getProperty("servicecomb.loadbalance.isolation.maxSingleTestWindow")).thenReturn("5000"); Assertions.assertEquals(5000, Configuration.INSTANCE.getMaxSingleTestWindow()); } } diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestExtensionsManager.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestExtensionsManager.java index c98808be334..2b159d38339 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestExtensionsManager.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestExtensionsManager.java @@ -19,47 +19,37 @@ import java.util.ArrayList; import java.util.List; -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; 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 org.mockito.Mockito; +import org.springframework.core.env.Environment; public class TestExtensionsManager { + Environment environment = Mockito.mock(Environment.class); + @BeforeEach public void setUp() { - ConfigUtil.createLocalConfig(); + LegacyPropertyFactory.setEnvironment(environment); } @AfterEach public void tearDown() { - ArchaiusUtils.resetConfig(); } @Test public void testRuleName() { - System.setProperty("servicecomb.loadbalance.mytest1.strategy.name", "RoundRobin"); - System.setProperty("servicecomb.loadbalance.mytest2.strategy.name", "Random"); - System.setProperty("servicecomb.loadbalance.mytest3.strategy.name", "WeightedResponse"); - System.setProperty("servicecomb.loadbalance.mytest4.strategy.name", "SessionStickiness"); - List extensionsFactories = new ArrayList<>(); - extensionsFactories.add(new RuleNameExtentionsFactory()); + extensionsFactories.add(new RuleNameExtensionsFactory()); ExtensionsManager extensionsManager = new ExtensionsManager(extensionsFactories); Assertions.assertEquals(RoundRobinRuleExt.class.getName(), - extensionsManager.createLoadBalancerRule("mytest1").getClass().getName()); + extensionsManager.createLoadBalancerRule("RoundRobin").getClass().getName()); Assertions.assertEquals(RandomRuleExt.class.getName(), - extensionsManager.createLoadBalancerRule("mytest2").getClass().getName()); + extensionsManager.createLoadBalancerRule("Random").getClass().getName()); Assertions.assertEquals(WeightedResponseTimeRuleExt.class.getName(), - extensionsManager.createLoadBalancerRule("mytest3").getClass().getName()); - Assertions.assertEquals(SessionStickinessRule.class.getName(), - extensionsManager.createLoadBalancerRule("mytest4").getClass().getName()); - - System.getProperties().remove("servicecomb.loadbalance.mytest1.strategy.name"); - System.getProperties().remove("servicecomb.loadbalance.mytest2.strategy.name"); - System.getProperties().remove("servicecomb.loadbalance.mytest3.strategy.name"); - System.getProperties().remove("servicecomb.loadbalance.mytest4.strategy.name"); + extensionsManager.createLoadBalancerRule("WeightedResponse").getClass().getName()); } } diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceCreator.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceCreator.java index b3a44bbeb1e..1ee1824c6d6 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceCreator.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceCreator.java @@ -23,16 +23,14 @@ import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.Transport; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; -import com.netflix.loadbalancer.Server; - -import mockit.Deencapsulation; import mockit.Expectations; import mockit.Injectable; -import org.junit.jupiter.api.Assertions; public class TestLoadBalanceCreator { @Test @@ -41,15 +39,18 @@ public void testLoadBalanceWithRoundRobinRuleAndFilter(@Injectable Invocation in // Robin components implementations require getReachableServers & getServerList have the same size, we add a test case for this. RoundRobinRuleExt rule = new RoundRobinRuleExt(); List servers = new ArrayList<>(); - Endpoint host1 = new Endpoint(transport, "host1"); - MicroserviceInstance instance1 = new MicroserviceInstance(); - instance1.setInstanceId("instance1"); - ServiceCombServer server = new ServiceCombServer(null, host1, instance1); - Endpoint host2 = new Endpoint(transport, "host2"); - MicroserviceInstance instance2 = new MicroserviceInstance(); - instance2.setInstanceId("instance2"); - ServiceCombServer server2 = new ServiceCombServer(null, host2, instance2); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance1 = new StatefulDiscoveryInstance(discoveryInstance); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("instance1"); + Endpoint host1 = new Endpoint(transport, "host1", instance1); + ServiceCombServer server = new ServiceCombServer(null, host1); + + DiscoveryInstance discoveryInstance2 = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance2 = new StatefulDiscoveryInstance(discoveryInstance2); + Mockito.when(discoveryInstance2.getInstanceId()).thenReturn("instance2"); + Endpoint host2 = new Endpoint(transport, "host2", instance2); + ServiceCombServer server2 = new ServiceCombServer(null, host2); servers.add(server); servers.add(server2); @@ -75,7 +76,7 @@ public void testLoadBalanceWithRoundRobinRuleAndFilter(@Injectable Invocation in result = servers; } }; - Server s = lb.chooseServer(invocation); + ServiceCombServer s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); @@ -91,15 +92,18 @@ public void testLoadBalanceWithRandomRuleAndFilter(@Injectable Invocation invoca LoadBalancer lb = new LoadBalancer(rule, "service"); List servers = new ArrayList<>(); - Endpoint host1 = new Endpoint(transport, "host1"); - MicroserviceInstance instance1 = new MicroserviceInstance(); - instance1.setInstanceId("instance1"); - ServiceCombServer server = new ServiceCombServer(null, host1, instance1); - Endpoint host2 = new Endpoint(transport, "host2"); - MicroserviceInstance instance2 = new MicroserviceInstance(); - instance2.setInstanceId("instance2"); - ServiceCombServer server2 = new ServiceCombServer(null, host2, instance2); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance1 = new StatefulDiscoveryInstance(discoveryInstance); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("instance1"); + Endpoint host1 = new Endpoint(transport, "host1", instance1); + ServiceCombServer server = new ServiceCombServer(null, host1); + + DiscoveryInstance discoveryInstance2 = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance2 = new StatefulDiscoveryInstance(discoveryInstance2); + Mockito.when(discoveryInstance2.getInstanceId()).thenReturn("instance2"); + Endpoint host2 = new Endpoint(transport, "host2", instance2); + ServiceCombServer server2 = new ServiceCombServer(null, host2); servers.add(server); servers.add(server2); @@ -122,7 +126,7 @@ public void testLoadBalanceWithRandomRuleAndFilter(@Injectable Invocation invoca result = servers; } }; - Server s = lb.chooseServer(invocation); + ServiceCombServer s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); @@ -138,22 +142,31 @@ public void testLoadBalanceWithWeightedResponseTimeRuleAndFilter(@Injectable End LoadBalancer lb = new LoadBalancer(rule, "service"); List servers = new ArrayList<>(); - MicroserviceInstance instance1 = new MicroserviceInstance(); - instance1.setInstanceId("ii01"); - MicroserviceInstance instance2 = new MicroserviceInstance(); - instance2.setInstanceId("ii02"); - ServiceCombServer server = new ServiceCombServer(null, endpoint1, instance1); - ServiceCombServer server2 = new ServiceCombServer(null, endpoint2, instance2); + + DiscoveryInstance discoveryInstance1 = Mockito.mock(DiscoveryInstance.class); +// StatefulDiscoveryInstance instance1 = new StatefulDiscoveryInstance(discoveryInstance1); + Mockito.when(discoveryInstance1.getInstanceId()).thenReturn("ii01"); + + DiscoveryInstance discoveryInstance2 = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance2 = new StatefulDiscoveryInstance(discoveryInstance2); + Mockito.when(discoveryInstance2.getInstanceId()).thenReturn("ii02"); new Expectations() { { endpoint1.getEndpoint(); result = "host1"; +// endpoint1.getMicroserviceInstance(); +// result = instance1; endpoint2.getEndpoint(); result = "host2"; + endpoint2.getMicroserviceInstance(); + result = instance2; } }; + ServiceCombServer server = new ServiceCombServer(null, endpoint1); + ServiceCombServer server2 = new ServiceCombServer(null, endpoint2); + servers.add(server); servers.add(server2); List filters = new ArrayList<>(); @@ -174,60 +187,11 @@ public void testLoadBalanceWithWeightedResponseTimeRuleAndFilter(@Injectable End result = servers; } }; - Server s = lb.chooseServer(invocation); + ServiceCombServer s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); } - - @Test - public void testLoadBalanceWithSessionSticknessRule(@Injectable Invocation invocation, - @Injectable Transport transport) { - SessionStickinessRule rule = new SessionStickinessRule(); - LoadBalancer lb = new LoadBalancer(rule, "service"); - - List servers = new ArrayList<>(); - Endpoint host1 = new Endpoint(transport, "host1"); - MicroserviceInstance instance1 = new MicroserviceInstance(); - ServiceCombServer server = new ServiceCombServer(null, host1, instance1); - instance1.setInstanceId("instance1"); - - Endpoint host2 = new Endpoint(transport, "host2"); - MicroserviceInstance instance2 = new MicroserviceInstance(); - ServiceCombServer server2 = new ServiceCombServer(null, host2, instance2); - instance2.setInstanceId("instance2"); - - servers.add(server); - servers.add(server2); - - lb.setFilters(new ArrayList<>()); - new Expectations() { - { - invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST); - result = servers; - } - }; - - Server s = lb.chooseServer(invocation); - Assertions.assertEquals(server, s); - s = lb.chooseServer(invocation); - Assertions.assertEquals(server, s); - - long time = Deencapsulation.getField(rule, "lastAccessedTime"); - Deencapsulation.setField(rule, "lastAccessedTime", time - 1000 * 300); - ArchaiusUtils.setProperty("cse.loadbalance.service.SessionStickinessRule.sessionTimeoutInSeconds", 9); - s = lb.chooseServer(invocation); - Assertions.assertEquals(server2, s); - - ArchaiusUtils.setProperty("cse.loadbalance.service.SessionStickinessRule.successiveFailedTimes", 5); - lb.getLoadBalancerStats().incrementSuccessiveConnectionFailureCount(s); - lb.getLoadBalancerStats().incrementSuccessiveConnectionFailureCount(s); - lb.getLoadBalancerStats().incrementSuccessiveConnectionFailureCount(s); - lb.getLoadBalancerStats().incrementSuccessiveConnectionFailureCount(s); - lb.getLoadBalancerStats().incrementSuccessiveConnectionFailureCount(s); - s = lb.chooseServer(invocation); - Assertions.assertEquals(server, s); - } } diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter.java index 32824acca1c..123f23ac514 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter.java @@ -17,29 +17,33 @@ package org.apache.servicecomb.loadbalance; +import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; +import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; + import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.transport.TransportManager; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.apache.servicecomb.registry.discovery.DiscoveryFilter; +import org.apache.servicecomb.registry.DiscoveryManager; +import org.apache.servicecomb.registry.discovery.DiscoveryTree; +import org.apache.servicecomb.registry.discovery.TelnetInstancePing; import org.apache.servicecomb.swagger.invocation.AsyncResponse; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; import mockit.Deencapsulation; -import mockit.Expectations; import mockit.Injectable; import mockit.Mock; import mockit.MockUp; @@ -70,8 +74,19 @@ public class TestLoadBalanceFilter { @Before public void setUp() { - ConfigUtil.installDynamicConfig(); - scbEngine = SCBBootstrap.createSCBEngineForTest().run(); + Environment environment = Mockito.mock(Environment.class); + scbEngine = SCBBootstrap.createSCBEngineForTest(environment); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)) + .thenReturn(BootStrapProperties.DEFAULT_APPLICATION); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)) + .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_NAME); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_ENVIRONMENT)) + .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_ENVIRONMENT); + Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); + Mockito.when(environment.getProperty("servicecomb.loadbalance.userDefinedEndpoint.enabled", + boolean.class, false)).thenReturn(false); + scbEngine.run(); transportManager = scbEngine.getTransportManager(); new MockUp(invocation) { @@ -98,25 +113,20 @@ Transport findTransport(String transportName) { } }; - new Expectations(SPIServiceUtils.class) { - { - SPIServiceUtils.getSortedService(DiscoveryFilter.class); - result = Collections.emptyList(); - } - }; - List extensionsFactories = new ArrayList<>(); - extensionsFactories.add(new RuleNameExtentionsFactory()); + extensionsFactories.add(new RuleNameExtensionsFactory()); ExtensionsManager extensionsManager = new ExtensionsManager(extensionsFactories); - handler = new LoadBalanceFilter(extensionsManager); + DiscoveryTree discoveryTree = new DiscoveryTree( + new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); + handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), + discoveryTree, scbEngine); loadBalancerMap = Deencapsulation.getField(handler, "loadBalancerMap"); } @After public void teardown() { scbEngine.destroy(); - ArchaiusUtils.resetConfig(); } @Test diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter2.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter2.java index ecb6b203421..4c2fb5f5b45 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter2.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter2.java @@ -20,161 +20,175 @@ import static org.mockito.Mockito.when; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.config.DynamicProperties; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.NonSwaggerInvocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; -import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import org.apache.servicecomb.core.transport.TransportManager; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.loadbalance.filter.ServerDiscoveryFilter; -import org.apache.servicecomb.localregistry.LocalRegistryStore; +import org.apache.servicecomb.loadbalance.filter.ZoneAwareDiscoveryFilter; import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.api.registry.DataCenterInfo; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstanceStatus; -import org.apache.servicecomb.registry.cache.InstanceCacheManager; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.discovery.DiscoveryTree; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.springframework.core.env.Environment; -import mockit.Mock; -import mockit.MockUp; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; public class TestLoadBalanceFilter2 { - private static SCBEngine scbEngine; - - @BeforeClass - public static void beforeClass() { - ConfigUtil.installDynamicConfig(); - ArchaiusUtils.setProperty("servicecomb.loadbalance.userDefinedEndpoint.enabled", "true"); - ArchaiusUtils.setProperty("servicecomb.loadbalance.filter.isolation.enabled", "true"); - scbEngine = SCBBootstrap.createSCBEngineForTest().run(); - } + DynamicProperties dynamicProperties = Mockito.mock(DynamicProperties.class); - @AfterClass - public static void afterClass() { - scbEngine.destroy(); - ArchaiusUtils.resetConfig(); - } + Environment environment = Mockito.mock(Environment.class); - @Before + @BeforeEach public void setUp() { - } - @After - public void teardown() { + Mockito.when(environment.getProperty("servicecomb.loadbalance.userDefinedEndpoint.enabled", + boolean.class, false)).thenReturn(false); + + Mockito.when(dynamicProperties.getBooleanProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.enabled"), + Mockito.any(), + Mockito.eq(true))) + .thenReturn(true); + Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratio"), + Mockito.any(), + Mockito.eq(30))) + .thenReturn(0); + Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratioCeiling"), + Mockito.any(), + Mockito.eq(100))) + .thenReturn(100); + + LegacyPropertyFactory.setEnvironment(environment); } @Test - public void testZoneAwareFilterWorks() throws Exception { + public void testZoneAwareFilterWorks() { ReferenceConfig referenceConfig = Mockito.mock(ReferenceConfig.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); InvocationRuntimeType invocationRuntimeType = Mockito.mock(InvocationRuntimeType.class); SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); + Operation operation = Mockito.mock(Operation.class); + when(operationMeta.getSwaggerOperation()).thenReturn(operation); + when(operation.getExtensions()).thenReturn(null); + OpenAPI openAPI = Mockito.mock(OpenAPI.class); + when(schemaMeta.getSwagger()).thenReturn(openAPI); + when(openAPI.getExtensions()).thenReturn(null); MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); when(schemaMeta.getMicroserviceName()).thenReturn("testMicroserviceName"); when(microserviceMeta.getAppId()).thenReturn("testApp"); - when(referenceConfig.getVersionRule()).thenReturn("0.0.0+"); when(referenceConfig.getTransport()).thenReturn("rest"); Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, new HashMap<>()); - - InstanceCacheManager instanceCacheManager = Mockito.mock(InstanceCacheManager.class); TransportManager transportManager = Mockito.mock(TransportManager.class); Transport transport = Mockito.mock(Transport.class); - ArchaiusUtils.setProperty("servicecomb.loadbalance.filter.operation.enabled", "false"); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); + Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); // set up data - MicroserviceInstance myself = new MicroserviceInstance(); - DataCenterInfo info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region"); - info.setAvailableZone("test-zone"); - myself.setDataCenterInfo(info); + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test-Region"); + myself.setAvailableZone("test-zone"); - MicroserviceInstance allmatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); + DataCenterInfo info = new DataCenterInfo(); info.setName("test"); info.setRegion("test-Region"); info.setAvailableZone("test-zone"); List allMatchEndpoint = new ArrayList<>(); allMatchEndpoint.add("rest://localhost:9090"); - allmatchInstance.setEndpoints(allMatchEndpoint); - allmatchInstance.setDataCenterInfo(info); - allmatchInstance.setInstanceId("allmatchInstance"); - - MicroserviceInstance regionMatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region"); - info.setAvailableZone("test-zone2"); + Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); + Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); + + DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); + DataCenterInfo regionMatchInfo = new DataCenterInfo(); + regionMatchInfo.setName("test"); + regionMatchInfo.setRegion("test-Region"); + regionMatchInfo.setAvailableZone("test-zone2"); List regionMatchEndpoint = new ArrayList<>(); regionMatchEndpoint.add("rest://localhost:9091"); - regionMatchInstance.setEndpoints(regionMatchEndpoint); - regionMatchInstance.setDataCenterInfo(info); - regionMatchInstance.setInstanceId("regionMatchInstance"); - - MicroserviceInstance noneMatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region2"); - info.setAvailableZone("test-zone2"); + Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); + Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); + Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); + + DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); + DataCenterInfo noneMatchInfo = new DataCenterInfo(); + noneMatchInfo.setName("test"); + noneMatchInfo.setRegion("test-Region2"); + noneMatchInfo.setAvailableZone("test-zone2"); List noMatchEndpoint = new ArrayList<>(); noMatchEndpoint.add("rest://localhost:9092"); - noneMatchInstance.setEndpoints(noMatchEndpoint); - noneMatchInstance.setDataCenterInfo(info); - noneMatchInstance.setInstanceId("noneMatchInstance"); + Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); + Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); + Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); - Map data = new HashMap<>(); + List data = new ArrayList<>(); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); - scbEngine.setTransportManager(transportManager); - LocalRegistryStore.INSTANCE.initSelfWithMocked(null, myself); - mockUpInstanceCacheManager(instanceCacheManager); - when(instanceCacheManager.getOrCreateVersionedCache("testApp", "testMicroserviceName", "0.0.0+")) - .thenReturn(parent); when(transportManager.findTransport("rest")).thenReturn(transport); - LoadBalanceFilter handler = null; - LoadBalancer loadBalancer = null; - ServiceCombServer server = null; + LoadBalanceFilter handler; + LoadBalancer loadBalancer; + ServiceCombServer server; - handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>())); + DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); + Mockito.when(discoveryManager.getOrCreateVersionedCache("testApp", "testMicroserviceName")) + .thenReturn(parent); + DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); + ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); + zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); + zoneAwareDiscoveryFilter.setDataCenterProperties(myself); + ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); + serverDiscoveryFilter.setScbEngine(scbEngine); + discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, + serverDiscoveryFilter)); + handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), + discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertNull(server); - data.put("noneMatchInstance", noneMatchInstance); + data.add(noneMatchInstance); parent.cacheVersion(1); - handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>())); + handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), + discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:9092", server.getEndpoint().getEndpoint()); - data.put("regionMatchInstance", regionMatchInstance); + data.add(regionMatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:9091", server.getEndpoint().getEndpoint()); - data.put("allmatchInstance", allmatchInstance); + data.add(allmatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); @@ -183,64 +197,75 @@ public void testZoneAwareFilterWorks() throws Exception { @Test - public void testIsolationEventWithEndpoint() throws Exception { + public void testIsolationEventWithEndpoint() { ReferenceConfig referenceConfig = Mockito.mock(ReferenceConfig.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); InvocationRuntimeType invocationRuntimeType = Mockito.mock(InvocationRuntimeType.class); SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); + Operation operation = Mockito.mock(Operation.class); + when(operationMeta.getSwaggerOperation()).thenReturn(operation); + when(operation.getExtensions()).thenReturn(null); + OpenAPI openAPI = Mockito.mock(OpenAPI.class); + when(schemaMeta.getSwagger()).thenReturn(openAPI); + when(openAPI.getExtensions()).thenReturn(null); MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); when(schemaMeta.getMicroserviceName()).thenReturn("testMicroserviceName"); when(microserviceMeta.getAppId()).thenReturn("testApp"); - when(referenceConfig.getVersionRule()).thenReturn("0.0.0+"); when(referenceConfig.getTransport()).thenReturn("rest"); Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, new HashMap<>()); - - InstanceCacheManager instanceCacheManager = Mockito.mock(InstanceCacheManager.class); TransportManager transportManager = Mockito.mock(TransportManager.class); Transport transport = Mockito.mock(Transport.class); - ArchaiusUtils.setProperty("servicecomb.loadbalance.filter.operation.enabled", "false"); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); + Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); // set up data - MicroserviceInstance myself = new MicroserviceInstance(); - DataCenterInfo info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test"); - info.setAvailableZone("test"); - myself.setDataCenterInfo(info); + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test"); + myself.setAvailableZone("test"); - MicroserviceInstance instance = new MicroserviceInstance(); - info = new DataCenterInfo(); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance = new StatefulDiscoveryInstance(discoveryInstance); + DataCenterInfo info = new DataCenterInfo(); info.setName("test"); info.setRegion("test"); info.setAvailableZone("test"); List allMatchEndpoint = new ArrayList<>(); allMatchEndpoint.add("rest://localhost:9090"); - instance.setEndpoints(allMatchEndpoint); - instance.setDataCenterInfo(info); - instance.setInstanceId("instance"); + Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); + Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("instance"); - Map data = new HashMap<>(); + List data = new ArrayList<>(); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); - scbEngine.setTransportManager(transportManager); - LocalRegistryStore.INSTANCE.initSelfWithMocked(null, myself); - mockUpInstanceCacheManager(instanceCacheManager); - when(instanceCacheManager.getOrCreateVersionedCache("testApp", "testMicroserviceName", "0.0.0+")) - .thenReturn(parent); when(transportManager.findTransport("rest")).thenReturn(transport); - LoadBalanceFilter handler = null; - LoadBalancer loadBalancer = null; - ServiceCombServer server = null; + LoadBalanceFilter handler; + LoadBalancer loadBalancer; + ServiceCombServer server; - handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>())); + DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); + Mockito.when(discoveryManager.getOrCreateVersionedCache("testApp", "testMicroserviceName")) + .thenReturn(parent); + DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); + ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); + zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); + zoneAwareDiscoveryFilter.setDataCenterProperties(myself); + ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); + serverDiscoveryFilter.setScbEngine(scbEngine); + discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, + serverDiscoveryFilter)); + handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), + discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertNull(server); - data.put("instance", instance); + data.add(instance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); @@ -248,8 +273,7 @@ public void testIsolationEventWithEndpoint() throws Exception { } @Test - public void testZoneAwareFilterWorksEmptyInstanceProtectionEnabled() throws Exception { - ArchaiusUtils.setProperty("servicecomb.loadbalance.filter.isolation.emptyInstanceProtectionEnabled", "true"); + public void testZoneAwareFilterWorksEmptyInstanceProtectionEnabled() { ReferenceConfig referenceConfig = Mockito.mock(ReferenceConfig.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); InvocationRuntimeType invocationRuntimeType = Mockito.mock(InvocationRuntimeType.class); @@ -258,90 +282,102 @@ public void testZoneAwareFilterWorksEmptyInstanceProtectionEnabled() throws Exce MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); when(schemaMeta.getMicroserviceName()).thenReturn("testMicroserviceName"); + Operation operation = Mockito.mock(Operation.class); + when(operationMeta.getSwaggerOperation()).thenReturn(operation); + when(operation.getExtensions()).thenReturn(null); + OpenAPI openAPI = Mockito.mock(OpenAPI.class); + when(schemaMeta.getSwagger()).thenReturn(openAPI); + when(openAPI.getExtensions()).thenReturn(null); when(microserviceMeta.getAppId()).thenReturn("testApp"); - when(referenceConfig.getVersionRule()).thenReturn("0.0.0+"); when(referenceConfig.getTransport()).thenReturn("rest"); Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, new HashMap<>()); - - InstanceCacheManager instanceCacheManager = Mockito.mock(InstanceCacheManager.class); TransportManager transportManager = Mockito.mock(TransportManager.class); Transport transport = Mockito.mock(Transport.class); - ArchaiusUtils.setProperty("servicecomb.loadbalance.filter.operation.enabled", "false"); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); + Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); // set up data - MicroserviceInstance myself = new MicroserviceInstance(); - DataCenterInfo info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region"); - info.setAvailableZone("test-zone"); - myself.setDataCenterInfo(info); + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test-Region"); + myself.setAvailableZone("test-zone"); - MicroserviceInstance allmatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); + DataCenterInfo info = new DataCenterInfo(); info.setName("test"); info.setRegion("test-Region"); info.setAvailableZone("test-zone"); List allMatchEndpoint = new ArrayList<>(); allMatchEndpoint.add("rest://localhost:9090"); - allmatchInstance.setEndpoints(allMatchEndpoint); - allmatchInstance.setDataCenterInfo(info); - allmatchInstance.setInstanceId("allmatchInstance"); - - MicroserviceInstance regionMatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region"); - info.setAvailableZone("test-zone2"); + Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); + Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); + + DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); + DataCenterInfo regionMatchInfo = new DataCenterInfo(); + regionMatchInfo.setName("test"); + regionMatchInfo.setRegion("test-Region"); + regionMatchInfo.setAvailableZone("test-zone2"); List regionMatchEndpoint = new ArrayList<>(); regionMatchEndpoint.add("rest://localhost:9091"); - regionMatchInstance.setEndpoints(regionMatchEndpoint); - regionMatchInstance.setDataCenterInfo(info); - regionMatchInstance.setInstanceId("regionMatchInstance"); - - MicroserviceInstance noneMatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region2"); - info.setAvailableZone("test-zone2"); + Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); + Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); + Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); + + DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); + DataCenterInfo noneMatchInfo = new DataCenterInfo(); + noneMatchInfo.setName("test"); + noneMatchInfo.setRegion("test-Region2"); + noneMatchInfo.setAvailableZone("test-zone2"); List noMatchEndpoint = new ArrayList<>(); noMatchEndpoint.add("rest://localhost:9092"); - noneMatchInstance.setEndpoints(noMatchEndpoint); - noneMatchInstance.setDataCenterInfo(info); - noneMatchInstance.setInstanceId("noneMatchInstance"); + Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); + Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); + Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); - Map data = new HashMap<>(); + List data = new ArrayList<>(); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); - scbEngine.setTransportManager(transportManager); - LocalRegistryStore.INSTANCE.initSelfWithMocked(null, myself); - mockUpInstanceCacheManager(instanceCacheManager); - when(instanceCacheManager.getOrCreateVersionedCache("testApp", "testMicroserviceName", "0.0.0+")) - .thenReturn(parent); when(transportManager.findTransport("rest")).thenReturn(transport); - LoadBalanceFilter handler = null; - LoadBalancer loadBalancer = null; - ServiceCombServer server = null; + LoadBalanceFilter handler; + LoadBalancer loadBalancer; + ServiceCombServer server; - handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>())); + DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); + Mockito.when(discoveryManager.getOrCreateVersionedCache("testApp", "testMicroserviceName")) + .thenReturn(parent); + DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); + ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); + zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); + zoneAwareDiscoveryFilter.setDataCenterProperties(myself); + ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); + serverDiscoveryFilter.setScbEngine(scbEngine); + discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, + serverDiscoveryFilter)); + handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertNull(server); - data.put("noneMatchInstance", noneMatchInstance); + data.add(noneMatchInstance); parent.cacheVersion(1); - handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>())); + handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:9092", server.getEndpoint().getEndpoint()); - data.put("regionMatchInstance", regionMatchInstance); + data.add(regionMatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:9091", server.getEndpoint().getEndpoint()); - data.put("allmatchInstance", allmatchInstance); + data.add(allmatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); @@ -349,91 +385,97 @@ public void testZoneAwareFilterWorksEmptyInstanceProtectionEnabled() throws Exce } @Test - public void testZoneAwareFilterUsingMockedInvocationWorks() throws Exception { - Invocation invocation = new NonSwaggerInvocation("testApp", "testMicroserviceName", "0.0.0+"); - - InstanceCacheManager instanceCacheManager = Mockito.mock(InstanceCacheManager.class); + public void testZoneAwareFilterUsingMockedInvocationWorks() { + Invocation invocation = new NonSwaggerInvocation("testApp", "testMicroserviceName"); TransportManager transportManager = Mockito.mock(TransportManager.class); Transport transport = Mockito.mock(Transport.class); - ArchaiusUtils.setProperty("servicecomb.loadbalance.filter.operation.enabled", "false"); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); + Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); + Mockito.when(environment.getProperty("servicecomb.loadbalance.filter.operation.enabled", + boolean.class, true)).thenReturn(false); // set up data - MicroserviceInstance myself = new MicroserviceInstance(); - DataCenterInfo info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region"); - info.setAvailableZone("test-zone"); - myself.setDataCenterInfo(info); + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test-Region"); + myself.setAvailableZone("test-zone"); - MicroserviceInstance allmatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); + DataCenterInfo info = new DataCenterInfo(); info.setName("test"); info.setRegion("test-Region"); info.setAvailableZone("test-zone"); List allMatchEndpoint = new ArrayList<>(); allMatchEndpoint.add("rest://localhost:7090"); - allmatchInstance.setEndpoints(allMatchEndpoint); - allmatchInstance.setDataCenterInfo(info); - allmatchInstance.setInstanceId("allmatchInstance"); - - MicroserviceInstance regionMatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region"); - info.setAvailableZone("test-zone2"); + Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); + Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); + + DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); + DataCenterInfo regionMatchInfo = new DataCenterInfo(); + regionMatchInfo.setName("test"); + regionMatchInfo.setRegion("test-Region"); + regionMatchInfo.setAvailableZone("test-zone2"); List regionMatchEndpoint = new ArrayList<>(); regionMatchEndpoint.add("rest://localhost:7091"); - regionMatchInstance.setEndpoints(regionMatchEndpoint); - regionMatchInstance.setDataCenterInfo(info); - regionMatchInstance.setInstanceId("regionMatchInstance"); - - MicroserviceInstance noneMatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region2"); - info.setAvailableZone("test-zone2"); + Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); + Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); + Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); + + DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); + DataCenterInfo noneMatchInfo = new DataCenterInfo(); + noneMatchInfo.setName("test"); + noneMatchInfo.setRegion("test-Region2"); + noneMatchInfo.setAvailableZone("test-zone2"); List noMatchEndpoint = new ArrayList<>(); noMatchEndpoint.add("rest://localhost:7092"); - noneMatchInstance.setEndpoints(noMatchEndpoint); - noneMatchInstance.setDataCenterInfo(info); - noneMatchInstance.setInstanceId("noneMatchInstance"); + Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); + Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); + Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); - Map data = new HashMap<>(); + List data = new ArrayList<>(); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); - scbEngine.setTransportManager(transportManager); - LocalRegistryStore.INSTANCE.initSelfWithMocked(null, myself); - mockUpInstanceCacheManager(instanceCacheManager); - when(instanceCacheManager.getOrCreateVersionedCache("testApp", "testMicroserviceName", "0.0.0+")) - .thenReturn(parent); when(transportManager.findTransport("rest")).thenReturn(transport); - LoadBalanceFilter handler = null; - LoadBalancer loadBalancer = null; - ServiceCombServer server = null; + LoadBalanceFilter handler; + LoadBalancer loadBalancer; + ServiceCombServer server; - DiscoveryTree discoveryTree = new DiscoveryTree(); - discoveryTree.addFilter(new ServerDiscoveryFilter()); - discoveryTree.sort(); - handler = new LoadBalanceFilter(discoveryTree, new ExtensionsManager(new ArrayList<>())); + DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); + Mockito.when(discoveryManager.getOrCreateVersionedCache("testApp", "testMicroserviceName")) + .thenReturn(parent); + DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); + ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); + zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); + zoneAwareDiscoveryFilter.setDataCenterProperties(myself); + ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); + serverDiscoveryFilter.setScbEngine(scbEngine); + discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, + serverDiscoveryFilter)); + handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertNull(server); - data.put("noneMatchInstance", noneMatchInstance); + data.add(noneMatchInstance); parent.cacheVersion(1); - handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>())); + handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:7092", server.getEndpoint().getEndpoint()); - data.put("regionMatchInstance", regionMatchInstance); + data.add(regionMatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:7091", server.getEndpoint().getEndpoint()); - data.put("allmatchInstance", allmatchInstance); + data.add(allmatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); @@ -441,106 +483,99 @@ public void testZoneAwareFilterUsingMockedInvocationWorks() throws Exception { } @Test - public void testStatusFilterUsingMockedInvocationWorks() throws Exception { - ArchaiusUtils.setProperty("servicecomb.loadbalance.filter.status.enabled", "false"); - - Invocation invocation = new NonSwaggerInvocation("testApp", "testMicroserviceName", "0.0.0+"); - - InstanceCacheManager instanceCacheManager = Mockito.mock(InstanceCacheManager.class); + public void testStatusFilterUsingMockedInvocationWorks() { + Invocation invocation = new NonSwaggerInvocation("testApp", "testMicroserviceName"); TransportManager transportManager = Mockito.mock(TransportManager.class); Transport transport = Mockito.mock(Transport.class); - ArchaiusUtils.setProperty("servicecomb.loadbalance.filter.operation.enabled", "false"); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); + Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); // set up data - MicroserviceInstance myself = new MicroserviceInstance(); - DataCenterInfo info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region"); - info.setAvailableZone("test-zone"); - myself.setDataCenterInfo(info); + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test-Region"); + myself.setAvailableZone("test-zone"); - MicroserviceInstance allmatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); + DataCenterInfo info = new DataCenterInfo(); info.setName("test"); info.setRegion("test-Region"); info.setAvailableZone("test-zone"); List allMatchEndpoint = new ArrayList<>(); allMatchEndpoint.add("rest://localhost:7090"); - allmatchInstance.setEndpoints(allMatchEndpoint); - allmatchInstance.setDataCenterInfo(info); - allmatchInstance.setInstanceId("allmatchInstance"); - allmatchInstance.setStatus(MicroserviceInstanceStatus.TESTING); - - MicroserviceInstance regionMatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region"); - info.setAvailableZone("test-zone2"); + Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); + Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); + Mockito.when(discoveryInstance.getStatus()).thenReturn(MicroserviceInstanceStatus.TESTING); + + DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); + DataCenterInfo regionMatchInfo = new DataCenterInfo(); + regionMatchInfo.setName("test"); + regionMatchInfo.setRegion("test-Region"); + regionMatchInfo.setAvailableZone("test-zone2"); List regionMatchEndpoint = new ArrayList<>(); regionMatchEndpoint.add("rest://localhost:7091"); - regionMatchInstance.setEndpoints(regionMatchEndpoint); - regionMatchInstance.setDataCenterInfo(info); - regionMatchInstance.setInstanceId("regionMatchInstance"); - - MicroserviceInstance noneMatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region2"); - info.setAvailableZone("test-zone2"); + Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); + Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); + Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); + + DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); + DataCenterInfo noneMatchInfo = new DataCenterInfo(); + noneMatchInfo.setName("test"); + noneMatchInfo.setRegion("test-Region2"); + noneMatchInfo.setAvailableZone("test-zone2"); List noMatchEndpoint = new ArrayList<>(); noMatchEndpoint.add("rest://localhost:7092"); - noneMatchInstance.setEndpoints(noMatchEndpoint); - noneMatchInstance.setDataCenterInfo(info); - noneMatchInstance.setInstanceId("noneMatchInstance"); + Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); + Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); + Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); - Map data = new HashMap<>(); + List data = new ArrayList<>(); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); - scbEngine.setTransportManager(transportManager); - LocalRegistryStore.INSTANCE.initSelfWithMocked(null, myself); - mockUpInstanceCacheManager(instanceCacheManager); - when(instanceCacheManager.getOrCreateVersionedCache("testApp", "testMicroserviceName", "0.0.0+")) - .thenReturn(parent); when(transportManager.findTransport("rest")).thenReturn(transport); - LoadBalanceFilter handler = null; - LoadBalancer loadBalancer = null; - ServiceCombServer server = null; + LoadBalanceFilter handler; + LoadBalancer loadBalancer; + ServiceCombServer server; - DiscoveryTree discoveryTree = new DiscoveryTree(); - discoveryTree.addFilter(new ServerDiscoveryFilter()); - discoveryTree.sort(); - handler = new LoadBalanceFilter(discoveryTree, new ExtensionsManager(new ArrayList<>())); + DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); + Mockito.when(discoveryManager.getOrCreateVersionedCache("testApp", "testMicroserviceName")) + .thenReturn(parent); + DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); + ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); + zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); + zoneAwareDiscoveryFilter.setDataCenterProperties(myself); + ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); + serverDiscoveryFilter.setScbEngine(scbEngine); + discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, + serverDiscoveryFilter)); + handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertNull(server); - data.put("noneMatchInstance", noneMatchInstance); + data.add(noneMatchInstance); parent.cacheVersion(1); - handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>())); + handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:7092", server.getEndpoint().getEndpoint()); - data.put("regionMatchInstance", regionMatchInstance); + data.add(regionMatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:7091", server.getEndpoint().getEndpoint()); - data.put("allmatchInstance", allmatchInstance); + data.add(allmatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:7090", server.getEndpoint().getEndpoint()); } - - private void mockUpInstanceCacheManager(InstanceCacheManager instanceCacheManager) { - new MockUp() { - @Mock - public InstanceCacheManager getInstanceCacheManager() { - return instanceCacheManager; - } - }; - } } diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalancer.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalancer.java index 7225c2eee6a..ba17976e0ba 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalancer.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalancer.java @@ -21,7 +21,8 @@ import java.util.List; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -34,18 +35,18 @@ public void testLoadBalancerFullOperationWithoutException() { List newServers = new ArrayList<>(); ServiceCombServer server = Mockito.mock(ServiceCombServer.class); Invocation invocation = Mockito.mock(Invocation.class); - MicroserviceInstance microserviceInstance = Mockito.mock(MicroserviceInstance.class); + StatefulDiscoveryInstance microserviceInstance = Mockito.mock(StatefulDiscoveryInstance.class); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); newServers.add(server); Mockito.when(invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST)).thenReturn(newServers); Mockito.when(server.getInstance()).thenReturn(microserviceInstance); - Mockito.when(microserviceInstance.getInstanceId()).thenReturn("123456"); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("123456"); LoadBalancer loadBalancer = new LoadBalancer(rule, "test"); loadBalancer.chooseServer(invocation); Mockito.when(rule.choose(newServers, invocation)).thenReturn(server); Assertions.assertEquals(server, loadBalancer.chooseServer(invocation)); - Assertions.assertNotNull(loadBalancer.getLoadBalancerStats()); Assertions.assertEquals("test", loadBalancer.getMicroServiceName()); } } diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadbalanceClientConfig.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadbalanceClientConfig.java deleted file mode 100644 index 41cab47ff10..00000000000 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadbalanceClientConfig.java +++ /dev/null @@ -1,32 +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.servicecomb.loadbalance; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestLoadbalanceClientConfig { - - @Test - public void testLoadbalanceConstructor() { - - LoadbalanceClientConfig lbc = new LoadbalanceClientConfig("test"); - - Assertions.assertNotNull(lbc); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestRoundRobinRuleExt.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestRoundRobinRuleExt.java index 9ab23a16c87..d381703f445 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestRoundRobinRuleExt.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestRoundRobinRuleExt.java @@ -30,14 +30,12 @@ public class TestRoundRobinRuleExt { @Test public void testRoundRobin() { RoundRobinRuleExt rule = new RoundRobinRuleExt(); - LoadBalancer loadBalancer = new LoadBalancer(rule, "testService"); List servers = new ArrayList<>(); Invocation invocation = Mockito.mock(Invocation.class); for (int i = 0; i < 2; i++) { ServiceCombServer server = Mockito.mock(ServiceCombServer.class); Mockito.when(server.toString()).thenReturn("server " + i); servers.add(server); - loadBalancer.getLoadBalancerStats().noteResponseTime(server, 1); } AtomicInteger server1 = new AtomicInteger(0); @@ -56,14 +54,12 @@ public void testRoundRobin() { public void testBenchmarkRobin() { // less than 0.001ms RoundRobinRuleExt rule = new RoundRobinRuleExt(); - LoadBalancer loadBalancer = new LoadBalancer(rule, "testService"); List servers = new ArrayList<>(); Invocation invocation = Mockito.mock(Invocation.class); for (int i = 0; i < 100; i++) { ServiceCombServer server = Mockito.mock(ServiceCombServer.class); Mockito.when(server.toString()).thenReturn("server " + i); servers.add(server); - loadBalancer.getLoadBalancerStats().noteResponseTime(server, 2); } long begin = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombServer.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombServer.java index f16516b982d..e914ae20c6a 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombServer.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombServer.java @@ -17,9 +17,10 @@ package org.apache.servicecomb.loadbalance; +import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Transport; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.cache.CacheEndpoint; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; @@ -37,9 +38,10 @@ public class TestServiceCombServer { @Before public void setUp() { - MicroserviceInstance instance = new MicroserviceInstance(); - instance.setInstanceId("123456"); - cs = new ServiceCombServer(null, transport, new CacheEndpoint("abcd", instance)); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance = new StatefulDiscoveryInstance(discoveryInstance); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("123456"); + cs = new ServiceCombServer(null, new Endpoint(transport, "abcd", instance)); } @Test @@ -57,14 +59,16 @@ public void testGetEndpoint() { public void testEqualsMethod() { Assertions.assertNotEquals(cs, (Object) "abcd"); - MicroserviceInstance instance1 = new MicroserviceInstance(); - instance1.setInstanceId("1234"); - ServiceCombServer other = new ServiceCombServer(null, transport, new CacheEndpoint("1234", instance1)); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance1 = new StatefulDiscoveryInstance(discoveryInstance); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("1234"); + ServiceCombServer other = new ServiceCombServer(null, new Endpoint(transport, "1234", instance1)); Assertions.assertNotEquals(cs, other); - MicroserviceInstance instance2 = new MicroserviceInstance(); - instance2.setInstanceId("123456"); - other = new ServiceCombServer(null, transport, new CacheEndpoint("abcd", instance2)); + DiscoveryInstance discoveryInstance2 = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance2 = new StatefulDiscoveryInstance(discoveryInstance2); + Mockito.when(discoveryInstance2.getInstanceId()).thenReturn("123456"); + other = new ServiceCombServer(null, new Endpoint(transport, "abcd", instance2)); Assertions.assertEquals(cs, other); } diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestSessionStickinessRule.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestSessionStickinessRule.java new file mode 100644 index 00000000000..9a60b18657c --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestSessionStickinessRule.java @@ -0,0 +1,223 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.loadbalance; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; + +import mockit.Deencapsulation; +import mockit.Mock; +import mockit.MockUp; + +public class TestSessionStickinessRule { + @Test + public void testServerWithoutTimeoutAndWithThreshold() { + + boolean status = true; + + SessionStickinessRule ss = new SessionStickinessRule(); + + Invocation invocation = mock(Invocation.class); + ServiceCombServer server = mock(ServiceCombServer.class); + List servers = new ArrayList<>(); + servers.add(server); + + Deencapsulation.setField(ss, "lastServer", server); + + ServerMetrics serverMetrics = new ServerMetrics(); + Mockito.when(server.getServerMetrics()).thenReturn(serverMetrics); + + new MockUp() { + + @Mock + private boolean isTimeOut() { + return false; + } + }; + + new MockUp() { + + @Mock + private boolean isErrorThresholdMet(ServiceCombServer server) { + return true; + } + }; + + try { + ss.choose(servers, invocation); + } catch (Exception e) { + e.printStackTrace(); + status = false; + } + Assertions.assertTrue(status); + } + + @Test + public void testServerWithTimeout() { + + boolean status = true; + + SessionStickinessRule ss = new SessionStickinessRule(); + + Invocation invocation = mock(Invocation.class); + ServiceCombServer server = mock(ServiceCombServer.class); + List servers = new ArrayList<>(); + servers.add(server); + + Deencapsulation.setField(ss, "lastServer", server); + + new MockUp() { + + @Mock + private boolean isTimeOut() { + return true; + } + }; + + try { + ss.choose(servers, invocation); + } catch (Exception e) { + status = false; + } + + Assertions.assertTrue(status); + } + + @Test + public void testServerWithoutTimeoutException() { + + boolean status = true; + + SessionStickinessRule ss = new SessionStickinessRule(); + + Invocation invocation = mock(Invocation.class); + ServiceCombServer server = mock(ServiceCombServer.class); + List servers = new ArrayList<>(); + servers.add(server); + + Deencapsulation.setField(ss, "lastServer", server); + + new MockUp() { + + @Mock + private boolean isTimeOut() { + return false; + } + }; + + try { + ss.choose(servers, invocation); + } catch (Exception e) { + status = false; + } + Assertions.assertFalse(status); + } + + @Test + public void testServerWithoutTimeoutAndThreshold() { + + boolean status = true; + + SessionStickinessRule ss = new SessionStickinessRule(); + + Invocation invocation = mock(Invocation.class); + ServiceCombServer server = mock(ServiceCombServer.class); + List servers = new ArrayList<>(); + servers.add(server); + + Deencapsulation.setField(ss, "lastServer", server); + + new MockUp() { + + @Mock + private boolean isTimeOut() { + return false; + } + }; + + new MockUp() { + + @Mock + private boolean isErrorThresholdMet(ServiceCombServer server) { + return false; + } + }; + + new MockUp() { + + @Mock + private boolean isLastServerExists(ServiceCombServer server) { + return true; + } + }; + + try { + ss.choose(servers, invocation); + } catch (Exception e) { + status = false; + } + Assertions.assertTrue(status); + } + + @Test + public void testLastServerNotExist() { + SessionStickinessRule rule = new SessionStickinessRule(); + + Transport transport = mock(Transport.class); + Invocation invocation = mock(Invocation.class); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance1 = new StatefulDiscoveryInstance(discoveryInstance); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("1234"); + ServiceCombServer mockedServer = + new ServiceCombServer(null, new Endpoint(transport, "rest:127.0.0.1:8890", instance1)); + List allServers = Arrays.asList(mockedServer); + LoadBalancer lb = new LoadBalancer(rule, "mockedServer"); + when(invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST)).thenReturn(allServers); + rule.setLoadBalancer(lb); + ServiceCombServer server = new ServiceCombServer(null, + new Endpoint(transport, "rest:127.0.0.1:8890", instance1)); + Deencapsulation.setField(rule, "lastServer", server); + + new MockUp(rule) { + @Mock + private boolean isTimeOut() { + return false; + } + + @Mock + private boolean isErrorThresholdMet(ServiceCombServer server) { + return false; + } + }; + ServiceCombServer s = rule.choose(allServers, invocation); + Assertions.assertEquals(mockedServer, s); + } +} diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestSessionSticknessRule.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestSessionSticknessRule.java deleted file mode 100644 index 3604d7b8635..00000000000 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestSessionSticknessRule.java +++ /dev/null @@ -1,270 +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.servicecomb.loadbalance; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.Transport; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.cache.CacheEndpoint; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; -import org.mockito.Mockito; - -import com.netflix.loadbalancer.LoadBalancerStats; -import com.netflix.loadbalancer.Server; - -import mockit.Deencapsulation; -import mockit.Mock; -import mockit.MockUp; - -public class TestSessionSticknessRule { - - @Test - public void testRuleFullOperation() { - SessionStickinessRule rule = new SessionStickinessRule(); - - LoadBalancer mockedLb = mock(LoadBalancer.class); - Transport transport = mock(Transport.class); - MicroserviceInstance instance1 = new MicroserviceInstance(); - instance1.setInstanceId("1234"); - ServiceCombServer mockedServer = - new ServiceCombServer(null, transport, new CacheEndpoint("rest:127.0.0.1:8889", instance1)); - Invocation invocation = mock(Invocation.class); - LoadBalancerStats stats = mock(LoadBalancerStats.class); - Mockito.when(mockedLb.getLoadBalancerStats()).thenReturn(stats); - rule.chooseServerWhenTimeout(Arrays.asList(mockedServer), invocation); - mockedServer.setAlive(true); - mockedServer.setReadyToServe(true); - List allServers = Arrays.asList(mockedServer); - rule.setLoadBalancer(mockedLb); - - Server s = rule.choose(allServers, invocation); - Assertions.assertEquals(s, mockedServer); - - s = rule.choose(allServers, invocation); - Assertions.assertEquals(s, mockedServer); - } - - @Test - public void testServerWithoutTimeoutAndWithThreshold() { - - boolean status = true; - - SessionStickinessRule ss = new SessionStickinessRule(); - - Invocation invocation = mock(Invocation.class); - ServiceCombServer server = mock(ServiceCombServer.class); - List servers = new ArrayList<>(); - servers.add(server); - - Deencapsulation.setField(ss, "lastServer", server); - - new MockUp() { - - @Mock - private boolean isTimeOut() { - return false; - } - }; - - new MockUp() { - - @Mock - private boolean isErrorThresholdMet() { - return true; - } - }; - - try { - ss.choose(servers, invocation); - } catch (Exception e) { - status = false; - } - Assertions.assertTrue(status); - } - - @Test - public void testServerWithTimeout() { - - boolean status = true; - - SessionStickinessRule ss = new SessionStickinessRule(); - - Invocation invocation = mock(Invocation.class); - ServiceCombServer server = mock(ServiceCombServer.class); - List servers = new ArrayList<>(); - servers.add(server); - - Deencapsulation.setField(ss, "lastServer", server); - - new MockUp() { - - @Mock - private boolean isTimeOut() { - return true; - } - }; - - try { - ss.choose(servers, invocation); - } catch (Exception e) { - status = false; - } - - Assertions.assertTrue(status); - } - - @Test - public void testServerWithoutTimeoutException() { - - boolean status = true; - - SessionStickinessRule ss = new SessionStickinessRule(); - - Invocation invocation = mock(Invocation.class); - ServiceCombServer server = mock(ServiceCombServer.class); - List servers = new ArrayList<>(); - servers.add(server); - - Deencapsulation.setField(ss, "lastServer", server); - - new MockUp() { - - @Mock - private boolean isTimeOut() { - return false; - } - }; - - try { - ss.choose(servers, invocation); - } catch (Exception e) { - status = false; - } - Assertions.assertFalse(status); - } - - @Test - public void testServerWithoutTimeoutAndThreshold() { - - boolean status = true; - - SessionStickinessRule ss = new SessionStickinessRule(); - - Invocation invocation = mock(Invocation.class); - ServiceCombServer server = mock(ServiceCombServer.class); - List servers = new ArrayList<>(); - servers.add(server); - - Deencapsulation.setField(ss, "lastServer", server); - - new MockUp() { - - @Mock - private boolean isTimeOut() { - return false; - } - }; - - new MockUp() { - - @Mock - private boolean isErrorThresholdMet() { - return false; - } - }; - - new MockUp() { - - @Mock - private boolean isLastServerExists(Server server) { - return true; - } - }; - - try { - ss.choose(servers, invocation); - } catch (Exception e) { - status = false; - } - Assertions.assertTrue(status); - } - - @Test - public void testServerWithActualServerObj() { - - boolean status = true; - SessionStickinessRule ss = new SessionStickinessRule(); - - Invocation invocation = mock(Invocation.class); - ServiceCombServer server = mock(ServiceCombServer.class); - List servers = new ArrayList<>(); - servers.add(server); - - Deencapsulation.setField(ss, "lastServer", server); - try { - ss.choose(servers, invocation); - } catch (Exception e) { - status = false; - } - Assertions.assertTrue(status); - } - - @Test - public void testLastServerNotExist() { - SessionStickinessRule rule = new SessionStickinessRule(); - - Transport transport = mock(Transport.class); - Invocation invocation = mock(Invocation.class); - MicroserviceInstance instance1 = new MicroserviceInstance(); - instance1.setInstanceId("1234"); - ServiceCombServer mockedServer = - new ServiceCombServer(null, transport, new CacheEndpoint("rest:127.0.0.1:8890", instance1)); - mockedServer.setAlive(true); - mockedServer.setReadyToServe(true); - mockedServer.setId("mockedServer"); - List allServers = Arrays.asList(mockedServer); - LoadBalancer lb = new LoadBalancer(rule, "mockedServer"); - when(invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST)).thenReturn(allServers); - rule.setLoadBalancer(lb); - ServiceCombServer server = new ServiceCombServer(null, transport, - new CacheEndpoint("rest:127.0.0.1:8890", instance1)); - Deencapsulation.setField(rule, "lastServer", server); - - new MockUp(rule) { - @Mock - private boolean isTimeOut() { - return false; - } - - @Mock - private boolean isErrorThresholdMet() { - return false; - } - }; - Server s = rule.choose(allServers, invocation); - Assertions.assertEquals(mockedServer, s); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestWeightedResponseTimeRuleExt.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestWeightedResponseTimeRuleExt.java index 26844d71bb5..e3d20d2eea2 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestWeightedResponseTimeRuleExt.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestWeightedResponseTimeRuleExt.java @@ -17,27 +17,31 @@ package org.apache.servicecomb.loadbalance; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + import org.apache.servicecomb.core.Invocation; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; +import io.github.resilience4j.core.metrics.Metrics.Outcome; public class TestWeightedResponseTimeRuleExt { @Test public void testRoundRobin() { WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); - LoadBalancer loadBalancer = new LoadBalancer(rule, "testService"); List servers = new ArrayList<>(); Invocation invocation = Mockito.mock(Invocation.class); for (int i = 0; i < 2; i++) { ServiceCombServer server = Mockito.mock(ServiceCombServer.class); + ServerMetrics serverMetrics = new ServerMetrics(); + Mockito.when(server.getServerMetrics()).thenReturn(serverMetrics); Mockito.when(server.toString()).thenReturn("server " + i); servers.add(server); - loadBalancer.getLoadBalancerStats().noteResponseTime(server, 1); + serverMetrics.record(1, TimeUnit.MILLISECONDS, Outcome.SUCCESS); } AtomicInteger server1 = new AtomicInteger(0); @@ -53,25 +57,30 @@ public void testRoundRobin() { } @Test - public void testWeighed() throws InterruptedException { + public void testWeighed() { WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); - LoadBalancer loadBalancer = new LoadBalancer(rule, "testService"); List servers = new ArrayList<>(); Invocation invocation = Mockito.mock(Invocation.class); ServiceCombServer server1 = Mockito.mock(ServiceCombServer.class); Mockito.when(server1.toString()).thenReturn("server " + 0); servers.add(server1); + ServerMetrics serverMetrics1 = new ServerMetrics(); + Mockito.when(server1.getServerMetrics()).thenReturn(serverMetrics1); + ServiceCombServer server2 = Mockito.mock(ServiceCombServer.class); Mockito.when(server2.toString()).thenReturn("server " + 1); servers.add(server2); + ServerMetrics serverMetrics2 = new ServerMetrics(); + Mockito.when(server2.getServerMetrics()).thenReturn(serverMetrics2); AtomicInteger serverCounter1 = new AtomicInteger(0); AtomicInteger serverCounter2 = new AtomicInteger(0); + for (int i = 0; i < 100; i++) { + serverMetrics1.record(200, TimeUnit.MILLISECONDS, Outcome.SUCCESS); + serverMetrics2.record(400, TimeUnit.MILLISECONDS, Outcome.SUCCESS); + } for (int i = 0; i < 2000; i++) { - loadBalancer.getLoadBalancerStats().noteResponseTime(server1, 20); - loadBalancer.getLoadBalancerStats().noteResponseTime(server2, 400); - Thread.sleep(1); if (rule.choose(servers, invocation).toString().equals("server 0")) { serverCounter1.incrementAndGet(); } else { @@ -80,16 +89,15 @@ public void testWeighed() throws InterruptedException { } double percent = (double) serverCounter1.get() / (serverCounter2.get() + serverCounter1.get()); System.out.println("percent" + percent); - Assertions.assertTrue(percent > 0.60d); - Assertions.assertTrue(percent < 0.90d); + Assertions.assertEquals(0.67d, percent, 0.1); serverCounter1.set(0); serverCounter2.set(0); - Thread.sleep(1000); + for (int i = 0; i < 100; i++) { + serverMetrics1.record(20, TimeUnit.MILLISECONDS, Outcome.SUCCESS); + serverMetrics2.record(20, TimeUnit.MILLISECONDS, Outcome.SUCCESS); + } for (int i = 0; i < 2000; i++) { - loadBalancer.getLoadBalancerStats().noteResponseTime(server1, 20); - loadBalancer.getLoadBalancerStats().noteResponseTime(server2, 20); - Thread.sleep(1); if (rule.choose(servers, invocation).toString().equals("server 0")) { serverCounter1.incrementAndGet(); } else { @@ -98,7 +106,7 @@ public void testWeighed() throws InterruptedException { } percent = (double) serverCounter1.get() / (serverCounter2.get() + serverCounter1.get()); System.out.println("percent" + percent); - Assertions.assertEquals(0.50d, percent, 0.2); + Assertions.assertEquals(0.50d, percent, 0.1); } @Test @@ -106,17 +114,18 @@ public void testBenchmark() { // 100 instances will taken less than 0.1ms. Because we use weighed rule when response time more than 10ms, // This only taken 1/1000 time. WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); - LoadBalancer loadBalancer = new LoadBalancer(rule, "testService"); List servers = new ArrayList<>(); Invocation invocation = Mockito.mock(Invocation.class); for (int i = 0; i < 100; i++) { ServiceCombServer server = Mockito.mock(ServiceCombServer.class); + ServerMetrics serverMetrics = new ServerMetrics(); Mockito.when(server.toString()).thenReturn("server " + i); + Mockito.when(server.getServerMetrics()).thenReturn(serverMetrics); servers.add(server); - loadBalancer.getLoadBalancerStats().noteResponseTime(server, i); + serverMetrics.record(i, TimeUnit.MILLISECONDS, Outcome.SUCCESS); } long begin = System.currentTimeMillis(); - for (int i = 0; i < 10000; i++) { + for (int i = 0; i < 100000; i++) { rule.choose(servers, invocation); } long taken = System.currentTimeMillis() - begin; @@ -128,17 +137,18 @@ public void testBenchmark() { public void testBenchmarkRobin() { // 100 instances will taken less than 0.02ms. Not as good as RoundRobinRule, which taken less than 0.001ms WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); - LoadBalancer loadBalancer = new LoadBalancer(rule, "testService"); List servers = new ArrayList<>(); Invocation invocation = Mockito.mock(Invocation.class); for (int i = 0; i < 100; i++) { ServiceCombServer server = Mockito.mock(ServiceCombServer.class); + ServerMetrics serverMetrics = new ServerMetrics(); Mockito.when(server.toString()).thenReturn("server " + i); + Mockito.when(server.getServerMetrics()).thenReturn(serverMetrics); servers.add(server); - loadBalancer.getLoadBalancerStats().noteResponseTime(server, 2); + serverMetrics.record(2, TimeUnit.MILLISECONDS, Outcome.SUCCESS); } long begin = System.currentTimeMillis(); - for (int i = 0; i < 10000; i++) { + for (int i = 0; i < 100000; i++) { rule.choose(servers, invocation); } long taken = System.currentTimeMillis() - begin; diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilterTest.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilterTest.java index 0ba2584b3b4..7c78388dcdc 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilterTest.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilterTest.java @@ -19,26 +19,30 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import org.apache.servicecomb.config.DynamicProperties; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; +import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.MutablePropertySources; import com.google.common.collect.Sets; -import mockit.Deencapsulation; -import mockit.Expectations; -import mockit.Injectable; - /** * Test for PriorityInstancePropertyDiscoveryFilter */ @@ -48,52 +52,67 @@ public class PriorityInstancePropertyDiscoveryFilterTest { private PriorityInstancePropertyDiscoveryFilter filter; - private Map instances; + private List instances; + + StatefulDiscoveryInstance instance1; - private MicroserviceInstance self; + ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); - @Injectable - RegistrationManager registrationManager; + EnumerablePropertySource propertySource; - RegistrationManager original; + DynamicProperties dynamicProperties = Mockito.mock(DynamicProperties.class); @Before public void setUp() { + propertySource = Mockito.mock(EnumerablePropertySource.class); + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + mutablePropertySources.addLast(propertySource); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); + Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { + "servicecomb.service.properties." + PROPERTY_KEY + }); + filter = new PriorityInstancePropertyDiscoveryFilter(); - instances = new HashMap<>(); - self = new MicroserviceInstance(); - self.setInstanceId("self"); - MicroserviceInstance instance1 = new MicroserviceInstance(); - instance1.setInstanceId("instance.empty"); - MicroserviceInstance instance2 = new MicroserviceInstance(); - instance2.getProperties().put(PROPERTY_KEY, "local"); - instance2.setInstanceId("instance.local"); - MicroserviceInstance instance3 = new MicroserviceInstance(); - instance3.getProperties().put(PROPERTY_KEY, "local.feature1"); - instance3.setInstanceId("instance.local.feature1"); - MicroserviceInstance instance4 = new MicroserviceInstance(); - instance4.getProperties().put(PROPERTY_KEY, "local.feature1.sprint1"); - instance4.setInstanceId("instance.local.feature1.sprint1"); - - instances.put(instance1.getInstanceId(), instance1); - instances.put(instance2.getInstanceId(), instance2); - instances.put(instance3.getInstanceId(), instance3); - instances.put(instance4.getInstanceId(), instance4); - - original = Deencapsulation.getField(RegistrationManager.class, "INSTANCE"); - Deencapsulation.setField(RegistrationManager.class, "INSTANCE", registrationManager); - - new Expectations() { - { - registrationManager.getMicroserviceInstance(); - result = self; - } - }; + filter.setEnvironment(environment); + filter.setDynamicProperties(dynamicProperties); + Mockito.when(dynamicProperties.getStringProperty(Mockito.eq("servicecomb.loadbalance.filter.priorityInstanceProperty.key"), + Mockito.any(), + Mockito.eq("environment"))).thenReturn("environment"); + instances = new ArrayList<>(); + filter.setEnvironment(environment); + DiscoveryInstance discoveryInstance1 = Mockito.mock(DiscoveryInstance.class); + instance1 = new StatefulDiscoveryInstance(discoveryInstance1); + Mockito.when(discoveryInstance1.getInstanceId()).thenReturn("instance.empty"); + + DiscoveryInstance discoveryInstance2 = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance2 = new StatefulDiscoveryInstance(discoveryInstance2); + Mockito.when(discoveryInstance2.getInstanceId()).thenReturn("instance.local"); + Map properties = new HashMap<>(); + properties.put(PROPERTY_KEY, "local"); + Mockito.when(discoveryInstance2.getProperties()).thenReturn(properties); + + DiscoveryInstance discoveryInstance3 = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance3 = new StatefulDiscoveryInstance(discoveryInstance3); + Mockito.when(discoveryInstance3.getInstanceId()).thenReturn("instance.local.feature1"); + Map properties3 = new HashMap<>(); + properties3.put(PROPERTY_KEY, "local.feature1"); + Mockito.when(discoveryInstance3.getProperties()).thenReturn(properties3); + + DiscoveryInstance discoveryInstance4 = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance4 = new StatefulDiscoveryInstance(discoveryInstance4); + Mockito.when(discoveryInstance4.getInstanceId()).thenReturn("instance.local.feature1.sprint1"); + Map properties4 = new HashMap<>(); + properties4.put(PROPERTY_KEY, "local.feature1.sprint1"); + Mockito.when(discoveryInstance4.getProperties()).thenReturn(properties4); + + instances.add(instance1); + instances.add(instance2); + instances.add(instance3); + instances.add(instance4); } @After public void cleanup() { - Deencapsulation.setField(RegistrationManager.class, "INSTANCE", original); } @Test @@ -113,25 +132,27 @@ public void testGetFilteredListOfServers() { executeTest("local.feature1.sprint2.temp", Sets.newHashSet("instance.local.feature1")); //none match - MicroserviceInstance instance1 = instances.remove("instance.empty"); + instances.remove(instance1); executeTest("", Collections.emptySet()); executeTest("foo", Collections.emptySet()); - instances.put("instance.empty", instance1); + instances.add(instance1); } private void executeTest(String selfProperty, Set expectedMatchedKeys) { + Mockito.when(environment.getProperty("servicecomb.service.properties." + PROPERTY_KEY)).thenReturn(selfProperty); + Invocation invocation = new Invocation(); DiscoveryContext discoveryContext = new DiscoveryContext(); discoveryContext.setInputParameters(invocation); - self.getProperties().put(PROPERTY_KEY, selfProperty); DiscoveryTreeNode parent = new DiscoveryTreeNode(); parent.name("parent"); parent.data(instances); DiscoveryTreeNode node = filter.discovery(discoveryContext, parent); - Map filterInstance = node.data(); - assertThat(filterInstance.keySet()).containsAnyElementsOf(expectedMatchedKeys); + List filterInstance = node.data(); + assertThat(filterInstance.stream().map(instance -> instance.getInstanceId()).collect( + Collectors.toList())).containsAnyElementsOf(expectedMatchedKeys); } } diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestInstancePropertyDiscoveryFilter.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestInstancePropertyDiscoveryFilter.java index 58840cabd81..0c498d25de2 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestInstancePropertyDiscoveryFilter.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestInstancePropertyDiscoveryFilter.java @@ -17,45 +17,60 @@ package org.apache.servicecomb.loadbalance.filter; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.commons.configuration.BaseConfiguration; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Injectable; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.MutablePropertySources; public class TestInstancePropertyDiscoveryFilter { private InstancePropertyDiscoveryFilter filter; - MicroserviceInstance instance = new MicroserviceInstance(); + StatefulDiscoveryInstance instance; - @BeforeClass - public static void beforeCls() { - AbstractConfiguration configuration = new BaseConfiguration(); - configuration.addProperty("servicecomb.loadbalance.test.flowsplitFilter.policy", - "org.apache.servicecomb.loadbalance.filter.SimpleFlowsplitFilter"); - configuration.addProperty("servicecomb.loadbalance.test.flowsplitFilter.options.tag0", "value0"); - } + ConfigurableEnvironment environment; + + EnumerablePropertySource propertySource; - @Before + @BeforeEach public void setUp() { + environment = Mockito.mock(ConfigurableEnvironment.class); + LegacyPropertyFactory.setEnvironment(environment); + propertySource = Mockito.mock(EnumerablePropertySource.class); + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + mutablePropertySources.addLast(propertySource); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); + Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { + "servicecomb.loadbalance.test.flowsplitFilter.policy", + "servicecomb.loadbalance.test.flowsplitFilter.options.tag0" + }); + Mockito.when(environment.getProperty("servicecomb.loadbalance.test.flowsplitFilter.policy")) + .thenReturn("org.apache.servicecomb.loadbalance.filter.SimpleFlowsplitFilter"); + Mockito.when(environment.getProperty("servicecomb.loadbalance.test.flowsplitFilter.options.tag0")) + .thenReturn("value0"); + filter = new InstancePropertyDiscoveryFilter(); Map properties = new HashMap<>(); properties.put("tag0", "value0"); properties.put("tag1", "value1"); - instance.setInstanceId("instance111"); - instance.setProperties(properties); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + instance = new StatefulDiscoveryInstance(discoveryInstance); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("instance111"); + Mockito.when(discoveryInstance.getProperties()).thenReturn(properties); } @Test @@ -76,22 +91,19 @@ public void testAllowVisit() { @Test @SuppressWarnings("unchecked") - public void testGetFilteredListOfServers(@Injectable DiscoveryContext context, @Injectable DiscoveryTreeNode parent, - @Injectable Invocation invocation) { - Map instances = new HashMap<>(); - instances.put(instance.getInstanceId(), instance); - new Expectations() { - { - context.getInputParameters(); - result = invocation; - parent.data(); - result = instances; - parent.name(); - result = "parent"; - } - }; + public void testGetFilteredListOfServers() { + DiscoveryContext context = new DiscoveryContext(); + DiscoveryTreeNode parent = new DiscoveryTreeNode(); + + Invocation invocation = Mockito.mock(Invocation.class); + context.setInputParameters(invocation); + + List instances = new ArrayList<>(); + instances.add(instance); + parent.data(instances); + parent.name("parent"); DiscoveryTreeNode node = filter.discovery(context, parent); - Assertions.assertEquals(1, ((Map) node.data()).keySet().size()); + Assertions.assertEquals(1, ((List) node.data()).size()); } } diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestServerDiscoveryFilter.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestServerDiscoveryFilter.java index 9cd20f2cfba..a4aa3a5191a 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestServerDiscoveryFilter.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestServerDiscoveryFilter.java @@ -17,81 +17,57 @@ package org.apache.servicecomb.loadbalance.filter; -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.core.Const; +import static org.mockito.ArgumentMatchers.any; + +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; -import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.transport.TransportManager; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.apache.servicecomb.loadbalance.ServiceCombServer; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; +import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.discovery.DiscoveryContext; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Injectable; -import mockit.Mocked; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; public class TestServerDiscoveryFilter { - static SCBEngine scbEngine; - - static TransportManager transportManager; - ServerDiscoveryFilter filter = new ServerDiscoveryFilter(); - @Mocked - Transport trasport; - - @BeforeClass - public static void setup() { - ConfigUtil.installDynamicConfig(); - scbEngine = SCBBootstrap.createSCBEngineForTest().run(); - transportManager = scbEngine.getTransportManager(); - } - - @AfterClass - public static void teardown() { - scbEngine.destroy(); - ArchaiusUtils.resetConfig(); - } - @Test public void createEndpoint_TransportNotExist() { - new Expectations(transportManager) { - { - transportManager.findTransport(anyString); - result = null; - } - }; - - ServiceCombServer server = (ServiceCombServer) filter.createEndpoint(null, Const.RESTFUL, null, null); + TransportManager transportManager = Mockito.mock(TransportManager.class); + Mockito.when(transportManager.findTransport(any(String.class))).thenReturn(null); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); + filter.setScbEngine(scbEngine); + ServiceCombServer server = (ServiceCombServer) filter.createEndpoint(null, CoreConst.RESTFUL, null, null); Assertions.assertNull(server); } @Test - public void createEndpointNormal(@Injectable DiscoveryContext context, @Injectable Invocation invocation) { - new Expectations(transportManager) { - { - transportManager.findTransport(anyString); - result = trasport; - context.getInputParameters(); - result = invocation; - invocation.getMicroserviceName(); - result = "test"; - } - }; - MicroserviceInstance instance = new MicroserviceInstance(); - instance.setInstanceId("0000001"); + public void createEndpointNormal() { + TransportManager transportManager = Mockito.mock(TransportManager.class); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); + Transport transport = Mockito.mock(Transport.class); + Invocation invocation = Mockito.mock(Invocation.class); + Mockito.when(transportManager.findTransport(any(String.class))).thenReturn(transport); + Mockito.when(invocation.getConfigTransportName()).thenReturn(CoreConst.RESTFUL); + Mockito.when(invocation.getMicroserviceName()).thenReturn("test"); + DiscoveryContext context = new DiscoveryContext(); + context.setInputParameters(invocation); + + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance instance = new StatefulDiscoveryInstance(discoveryInstance); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("0000001"); + filter.setScbEngine(scbEngine); ServiceCombServer server = (ServiceCombServer) filter - .createEndpoint(context, Const.RESTFUL, "rest://localhost:8080", instance); + .createEndpoint(context, CoreConst.RESTFUL, "rest://localhost:8080", instance); Assertions.assertSame(instance, server.getInstance()); - Assertions.assertSame(trasport, server.getEndpoint().getTransport()); + Assertions.assertSame(transport, server.getEndpoint().getTransport()); Assertions.assertEquals("rest://localhost:8080", server.getEndpoint().getEndpoint()); } } diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestZoneAwareDiscoveryFilter.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestZoneAwareDiscoveryFilter.java new file mode 100644 index 00000000000..05b6d5432ca --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestZoneAwareDiscoveryFilter.java @@ -0,0 +1,197 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.loadbalance.filter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.config.DynamicProperties; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.registry.discovery.DiscoveryContext; +import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestZoneAwareDiscoveryFilter { + DynamicProperties dynamicProperties = Mockito.mock(DynamicProperties.class); + + @BeforeEach + public void setUp() { + + Mockito.when(dynamicProperties.getBooleanProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.enabled"), + Mockito.any(), + Mockito.eq(true))) + .thenReturn(true); + } + + @Test + public void test_not_enough_instance() { + + Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratio"), + Mockito.any(), + Mockito.eq(30))) + .thenReturn(50); + Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratioCeiling"), + Mockito.any(), + Mockito.eq(50))) + .thenReturn(70); + + ZoneAwareDiscoveryFilter filter = new ZoneAwareDiscoveryFilter(); + filter.setDynamicProperties(dynamicProperties); + + // set up data + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test-Region"); + myself.setAvailableZone("test-zone"); + filter.setDataCenterProperties(myself); + + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); + DataCenterInfo info = new DataCenterInfo(); + info.setName("test"); + info.setRegion("test-Region"); + info.setAvailableZone("test-zone"); + List allMatchEndpoint = new ArrayList<>(); + allMatchEndpoint.add("rest://localhost:9090"); + Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); + Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); + + DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); + DataCenterInfo regionMatchInfo = new DataCenterInfo(); + regionMatchInfo.setName("test"); + regionMatchInfo.setRegion("test-Region"); + regionMatchInfo.setAvailableZone("test-zone2"); + List regionMatchEndpoint = new ArrayList<>(); + regionMatchEndpoint.add("rest://localhost:9091"); + Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); + Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); + Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); + + DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); + DataCenterInfo noneMatchInfo = new DataCenterInfo(); + noneMatchInfo.setName("test"); + noneMatchInfo.setRegion("test-Region2"); + noneMatchInfo.setAvailableZone("test-zone2"); + List noMatchEndpoint = new ArrayList<>(); + noMatchEndpoint.add("rest://localhost:9092"); + Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); + Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); + Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); + + // run test + List data = Arrays.asList(allmatchInstance, regionMatchInstance, noneMatchInstance); + DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); + DiscoveryContext context = new DiscoveryContext(); + DiscoveryTreeNode result = filter.discovery(context, parent); + + // check result + Integer level = context.getContextParameter(filter.contextParameter()); + Integer groups = parent.attribute(filter.groupsSizeParameter()); + List resultData = result.data(); + Assertions.assertEquals(1, level); + Assertions.assertEquals(2, groups); + Assertions.assertEquals(2, resultData.size()); + Assertions.assertEquals("regionMatchInstance", resultData.get(0).getInstanceId()); + Assertions.assertEquals("allmatchInstance", resultData.get(1).getInstanceId()); + } + + @Test + public void test_not_enough_instance_both_ceiling_floor() { + + Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratio"), + Mockito.any(), + Mockito.eq(30))) + .thenReturn(40); + Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratioCeiling"), + Mockito.any(), + Mockito.eq(60))) + .thenReturn(60); + + ZoneAwareDiscoveryFilter filter = new ZoneAwareDiscoveryFilter(); + filter.setDynamicProperties(dynamicProperties); + + // set up data + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test-Region"); + myself.setAvailableZone("test-zone"); + filter.setDataCenterProperties(myself); + + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); + DataCenterInfo info = new DataCenterInfo(); + info.setName("test"); + info.setRegion("test-Region"); + info.setAvailableZone("test-zone"); + List allMatchEndpoint = new ArrayList<>(); + allMatchEndpoint.add("rest://localhost:9090"); + Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); + Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); + + DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); + DataCenterInfo regionMatchInfo = new DataCenterInfo(); + regionMatchInfo.setName("test"); + regionMatchInfo.setRegion("test-Region"); + regionMatchInfo.setAvailableZone("test-zone2"); + List regionMatchEndpoint = new ArrayList<>(); + regionMatchEndpoint.add("rest://localhost:9091"); + Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); + Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); + Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); + + DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); + DataCenterInfo noneMatchInfo = new DataCenterInfo(); + noneMatchInfo.setName("test"); + noneMatchInfo.setRegion("test-Region2"); + noneMatchInfo.setAvailableZone("test-zone2"); + List noMatchEndpoint = new ArrayList<>(); + noMatchEndpoint.add("rest://localhost:9092"); + Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); + Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); + Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); + + // run test + List data = Arrays.asList(allmatchInstance, regionMatchInstance, noneMatchInstance); + DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); + DiscoveryContext context = new DiscoveryContext(); + DiscoveryTreeNode result = filter.discovery(context, parent); + + // check result + Integer level = context.getContextParameter(filter.contextParameter()); + Integer groups = parent.attribute(filter.groupsSizeParameter()); + List resultData = result.data(); + Assertions.assertEquals(null, level); + Assertions.assertEquals(1, groups); + Assertions.assertEquals(3, resultData.size()); + Assertions.assertEquals("noneMatchInstance", resultData.get(0).getInstanceId()); + Assertions.assertEquals("regionMatchInstance", resultData.get(1).getInstanceId()); + Assertions.assertEquals("allmatchInstance", resultData.get(2).getInstanceId()); + } +} diff --git a/handlers/handler-loadbalance/src/test/resources/microservice.yaml b/handlers/handler-loadbalance/src/test/resources/microservice.yaml index 4db1321ba99..5ff7ed9ae41 100644 --- a/handlers/handler-loadbalance/src/test/resources/microservice.yaml +++ b/handlers/handler-loadbalance/src/test/resources/microservice.yaml @@ -20,4 +20,4 @@ servicecomb: consumer: default: empty producer: - default: empty \ No newline at end of file + default: empty diff --git a/handlers/handler-publickey-auth/pom.xml b/handlers/handler-publickey-auth/pom.xml index 03735e95665..9f13b26f232 100644 --- a/handlers/handler-publickey-auth/pom.xml +++ b/handlers/handler-publickey-auth/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb handlers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT handler-publickey-auth diff --git a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationBootListener.java b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationBootListener.java index 162fab974bb..71fd4e500fb 100644 --- a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationBootListener.java +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationBootListener.java @@ -22,11 +22,19 @@ import org.apache.servicecomb.foundation.token.Keypair4Auth; import org.apache.servicecomb.registry.RegistrationManager; import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.springframework.beans.factory.annotation.Autowired; /** * initialize public and private key pair when system boot before registry instance to service center */ public class AuthenticationBootListener implements BootListener { + private RegistrationManager registrationManager; + + @Autowired + public void setRegistrationManager(RegistrationManager registrationManager) { + this.registrationManager = registrationManager; + } + @Override public void onBootEvent(BootEvent event) { if (!EventType.BEFORE_REGISTRY.equals(event.getEventType())) { @@ -36,7 +44,7 @@ public void onBootEvent(BootEvent event) { Keypair4Auth.INSTANCE.setPrivateKey(rsaKeyPairEntry.getPrivateKey()); Keypair4Auth.INSTANCE.setPublicKey(rsaKeyPairEntry.getPublicKey()); Keypair4Auth.INSTANCE.setPublicKeyEncoded(rsaKeyPairEntry.getPublicKeyEncoded()); - RegistrationManager.INSTANCE.getMicroserviceInstance().getProperties().put(DefinitionConst.INSTANCE_PUBKEY_PRO, + this.registrationManager.addProperty(DefinitionConst.INSTANCE_PUBKEY_PRO, rsaKeyPairEntry.getPublicKeyEncoded()); } } diff --git a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java index cd69d415003..a7f8ea308ee 100644 --- a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java @@ -17,10 +17,14 @@ package org.apache.servicecomb.authentication; import org.apache.servicecomb.authentication.consumer.ConsumerAuthFilter; +import org.apache.servicecomb.authentication.consumer.ConsumerTokenManager; +import org.apache.servicecomb.authentication.provider.AccessController; import org.apache.servicecomb.authentication.provider.ProviderAuthFilter; +import org.apache.servicecomb.authentication.provider.ProviderTokenManager; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; @Configuration @ConditionalOnProperty(value = AuthenticationConfiguration.ACCESS_CONTROL_ENABLED, @@ -31,17 +35,32 @@ public class AuthenticationConfiguration { public static final String ACCESS_CONTROL_ENABLED = ACCESS_CONTROL_PREFIX + ".enabled"; @Bean - public ConsumerAuthFilter consumerAuthFilter() { + public ConsumerAuthFilter scbConsumerAuthFilter() { return new ConsumerAuthFilter(); } @Bean - public ProviderAuthFilter providerAuthFilter() { + public ProviderAuthFilter scbProviderAuthFilter() { return new ProviderAuthFilter(); } @Bean - public AuthenticationBootListener authenticationBootListener() { + public AuthenticationBootListener scbAuthenticationBootListener() { return new AuthenticationBootListener(); } + + @Bean + public ConsumerTokenManager scbConsumerTokenManager() { + return new ConsumerTokenManager(); + } + + @Bean + public ProviderTokenManager scbProviderTokenManager() { + return new ProviderTokenManager(); + } + + @Bean + public AccessController scbAccessController(Environment environment) { + return new AccessController(environment); + } } diff --git a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerAuthFilter.java b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerAuthFilter.java index cc43b64aef1..e26f24d936a 100644 --- a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerAuthFilter.java +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerAuthFilter.java @@ -19,27 +19,32 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; -import javax.ws.rs.core.Response.Status; - -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.beans.factory.annotation.Autowired; + +import jakarta.ws.rs.core.Response.Status; -public class ConsumerAuthFilter implements ConsumerFilter { - private ConsumerTokenManager authenticationTokenManager = new ConsumerTokenManager(); +public class ConsumerAuthFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { + private ConsumerTokenManager authenticationTokenManager; + + @Autowired + public void setConsumerTokenManager(ConsumerTokenManager consumerTokenManager) { + this.authenticationTokenManager = consumerTokenManager; + } @Override - public int getOrder(InvocationType invocationType, String microservice) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1010; } - @Nonnull @Override public String getName() { return "consumer-public-key"; @@ -52,7 +57,7 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne return CompletableFuture.failedFuture( new InvocationException(Status.SERVICE_UNAVAILABLE, "auth token is not properly configured yet.")); } - invocation.addContext(Const.AUTH_TOKEN, token.get()); + invocation.addContext(CoreConst.AUTH_TOKEN, token.get()); return nextNode.onFilter(invocation); } } diff --git a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerTokenManager.java b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerTokenManager.java index c78f3a8c006..8da889abc02 100644 --- a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerTokenManager.java +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerTokenManager.java @@ -19,11 +19,13 @@ import java.security.PrivateKey; import org.apache.servicecomb.authentication.RSAAuthenticationToken; +import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.foundation.common.utils.KeyPairUtils; import org.apache.servicecomb.foundation.token.Keypair4Auth; -import org.apache.servicecomb.registry.RegistrationManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; public class ConsumerTokenManager { @@ -33,6 +35,13 @@ public class ConsumerTokenManager { private RSAAuthenticationToken token; + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + public String getToken() { if (isExpired(token)) { @@ -47,8 +56,8 @@ public String getToken() { public String createToken() { PrivateKey privateKey = Keypair4Auth.INSTANCE.getPrivateKey(); - String instanceId = RegistrationManager.INSTANCE.getMicroserviceInstance().getInstanceId(); - String serviceId = RegistrationManager.INSTANCE.getMicroservice().getServiceId(); + String instanceId = BootStrapProperties.readServiceName(environment); + String serviceId = BootStrapProperties.readApplication(environment); if (instanceId == null || serviceId == null) { LOGGER.error("service not ready when create token."); diff --git a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/AccessController.java b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/AccessController.java index 41170bebe4f..b923b2939d7 100644 --- a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/AccessController.java +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/AccessController.java @@ -16,20 +16,22 @@ */ package org.apache.servicecomb.authentication.provider; -import java.beans.PropertyDescriptor; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.core.Const; -import org.apache.servicecomb.registry.api.registry.Microservice; +import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.DynamicPropertyFactory; +import com.google.common.eventbus.Subscribe; /** * Add black / white list control to service access @@ -57,34 +59,38 @@ static class ConfigurationItem { private static final String KEY_RULE_POSTFIX = ".rule"; + private final Environment environment; + private Map whiteList = new HashMap<>(); private Map blackList = new HashMap<>(); - public AccessController() { + public AccessController(Environment environment) { + this.environment = environment; loadConfigurations(KEY_BLACK_LIST_PREFIX); loadConfigurations(KEY_WHITE_LIST_PREFIX); + EventManager.register(this); } - public boolean isAllowed(Microservice microservice) { + public boolean isAllowed(DiscoveryInstance microservice) { return whiteAllowed(microservice) && !blackDenied(microservice); } - private boolean whiteAllowed(Microservice microservice) { + private boolean whiteAllowed(DiscoveryInstance microservice) { if (whiteList.isEmpty()) { return true; } return matchFound(microservice, whiteList); } - private boolean blackDenied(Microservice microservice) { + private boolean blackDenied(DiscoveryInstance microservice) { if (blackList.isEmpty()) { return false; } return matchFound(microservice, blackList); } - private boolean matchFound(Microservice microservice, Map ruleList) { + private boolean matchFound(DiscoveryInstance microservice, Map ruleList) { boolean matched = false; for (ConfigurationItem item : ruleList.values()) { if (ConfigurationItem.CATEGORY_PROPERTY.equals(item.category)) { @@ -97,7 +103,7 @@ private boolean matchFound(Microservice microservice, Map properties = microservice.getProperties(); for (Entry entry : properties.entrySet()) { if (!entry.getKey().equals(item.propertyName)) { @@ -108,25 +114,16 @@ private boolean matchMicroserviceProperties(Microservice microservice, Configura return false; } - private boolean matchMicroserviceField(Microservice microservice, ConfigurationItem item) { - Object fieldValue = null; - try { - fieldValue = new PropertyDescriptor(item.propertyName, Microservice.class).getReadMethod().invoke(microservice); - } catch (Exception e) { - if (DynamicPropertyFactory.getInstance().getBooleanProperty(Const.PRINT_SENSITIVE_ERROR_MESSAGE, - false).get()) { - LOG.warn("can't find propertyname: {} in microservice field, will search in microservice properties.", - item.propertyName, e); - } else { - LOG.warn("can't find propertyname: {} in microservice field, will search in microservice properties.", - item.propertyName); - } - return false; - } - if (fieldValue.getClass().getName().equals(String.class.getName())) { - return isPatternMatch((String) fieldValue, item.rule); + private boolean matchMicroserviceField(DiscoveryInstance microservice, ConfigurationItem item) { + String fieldValue; + if ("version".equals(item.propertyName)) { + fieldValue = microservice.getVersion(); + } else if ("serviceName".equals(item.propertyName)) { + fieldValue = microservice.getServiceName(); + } else { + fieldValue = microservice.getProperties().get(item.propertyName); } - return false; + return isPatternMatch(fieldValue, item.rule); } private boolean isPatternMatch(String value, String pattern) { @@ -139,40 +136,40 @@ private boolean isPatternMatch(String value, String pattern) { return value.equals(pattern); } - private void loadConfigurations(String prefix) { - ConcurrentCompositeConfiguration config = (ConcurrentCompositeConfiguration) DynamicPropertyFactory - .getBackingConfigurationSource(); - loadConfigurations(config, prefix); - config.addConfigurationListener(event -> { - if (event.getPropertyName().startsWith(prefix)) { - LOG.info("Access rule have been changed. Reload configurations. Event=" + event.getType()); - loadConfigurations(config, prefix); + @Subscribe + public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { + for (String changed : event.getChanged()) { + if (changed.startsWith(KEY_WHITE_LIST_PREFIX)) { + loadConfigurations(KEY_WHITE_LIST_PREFIX); + break; + } + } + for (String changed : event.getChanged()) { + if (changed.startsWith(KEY_BLACK_LIST_PREFIX)) { + loadConfigurations(KEY_BLACK_LIST_PREFIX); + break; } - }); + } } - private void loadConfigurations(ConcurrentCompositeConfiguration config, String prefix) { + private void loadConfigurations(String prefix) { Map configurations = new HashMap<>(); - Iterator configsItems = config.getKeys(prefix); - while (configsItems.hasNext()) { - String pathKey = configsItems.next(); + Set configsItems = ConfigUtil.propertiesWithPrefix((ConfigurableEnvironment) environment, prefix); + for (String pathKey : configsItems) { if (pathKey.endsWith(KEY_RULE_POSTFIX)) { ConfigurationItem configurationItem = new ConfigurationItem(); - String rule = DynamicPropertyFactory.getInstance() - .getStringProperty(pathKey, null).get(); + String rule = environment.getProperty(pathKey); if (StringUtils.isEmpty(rule)) { continue; } configurationItem.rule = rule; String pathKeyItem = pathKey .substring(prefix.length() + 1, pathKey.length() - KEY_RULE_POSTFIX.length()); - configurationItem.propertyName = DynamicPropertyFactory.getInstance() - .getStringProperty(String.format(KEY_PROPERTY_NAME, prefix, pathKeyItem), null).get(); + configurationItem.propertyName = environment.getProperty(String.format(KEY_PROPERTY_NAME, prefix, pathKeyItem)); if (StringUtils.isEmpty(configurationItem.propertyName)) { continue; } - configurationItem.category = DynamicPropertyFactory.getInstance() - .getStringProperty(String.format(KEY_CATEGORY, prefix, pathKeyItem), null).get(); + configurationItem.category = environment.getProperty(String.format(KEY_CATEGORY, prefix, pathKeyItem)); if (StringUtils.isEmpty(configurationItem.category)) { continue; } @@ -191,8 +188,8 @@ private void loadConfigurations(ConcurrentCompositeConfiguration config, String private void logConfigurations(Map configurations, boolean isWhite) { configurations.forEach((key, item) -> LOG.info((isWhite ? "White list " : "Black list ") + "config item: key=" + key - + ";category=" + item.category - + ";propertyName=" + item.propertyName - + ";rule=" + item.rule)); + + ";category=" + item.category + + ";propertyName=" + item.propertyName + + ";rule=" + item.rule)); } } diff --git a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/PathCheckUtils.java b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/PathCheckUtils.java new file mode 100644 index 00000000000..c587195d238 --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/PathCheckUtils.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.authentication.provider; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.env.Environment; + +public class PathCheckUtils { + private static final String KEY_INCLUDE_PATH = "servicecomb.publicKey.accessControl.includePathPatterns"; + + private static final String KEY_EXCLUDE_PATH = "servicecomb.publicKey.accessControl.excludePathPatterns"; + + /** + * first determine configured non-authentication path is matched requestPath, if match not needed auth. + * second determine whether of configured authentication path, if not configured, default all path need auth; + * if configured, then check whether of matched requestPath, if match needed auth, otherwise not needed auth. + * + * @param requestPath path + * @param env environment + * @return notRequiredAuth + */ + public static boolean isNotRequiredAuth(String requestPath, Environment env) { + if (excludePathMatchPath(requestPath, env)) { + return true; + } + return includePathMatchPath(requestPath, env); + } + + private static boolean excludePathMatchPath(String requestPath, Environment env) { + String excludePathPattern = env.getProperty(KEY_EXCLUDE_PATH, ""); + if (StringUtils.isEmpty(excludePathPattern)) { + return false; + } + return isPathMather(requestPath, excludePathPattern); + } + + private static boolean includePathMatchPath(String requestPath, Environment env) { + String includePathPattern = env.getProperty(KEY_INCLUDE_PATH, ""); + if (StringUtils.isEmpty(includePathPattern)) { + return false; + } + return !isPathMather(requestPath, includePathPattern); + } + + private static boolean isPathMather(String requestPath, String pathPattern) { + for (String pattern : pathPattern.split(",")) { + if (!pattern.isEmpty() && isPatternMatch(requestPath, pattern)) { + return true; + } + } + return false; + } + + public static boolean isPatternMatch(String value, String pattern) { + if (pattern.startsWith("*") || pattern.startsWith("/*")) { + int index = 0; + for (int i = 0; i < pattern.length(); i++) { + if (pattern.charAt(i) != '*' && pattern.charAt(i) != '/') { + break; + } + index++; + } + return value.endsWith(pattern.substring(index)); + } + if (pattern.endsWith("*")) { + int index = pattern.length() - 1; + for (int i = pattern.length() - 1; i >= 0; i--) { + if (pattern.charAt(i) != '*' && pattern.charAt(i) != '/') { + break; + } + index--; + } + return value.startsWith(pattern.substring(0, index + 1)); + } + return value.equals(pattern); + } +} diff --git a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java index 54923106ce6..f525ae88cf8 100644 --- a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java @@ -18,27 +18,36 @@ import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; -import javax.ws.rs.core.Response.Status; - -import org.apache.servicecomb.core.Const; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.filter.ProducerFilter; -import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +import jakarta.ws.rs.core.Response.Status; -public class ProviderAuthFilter implements ProducerFilter { - private final ProviderTokenManager authenticationTokenManager = new ProviderTokenManager(); +public class ProviderAuthFilter extends AbstractFilter implements ProviderFilter { + private ProviderTokenManager authenticationTokenManager; + + private Environment env; + + @Autowired + public void setProviderTokenManager(ProviderTokenManager providerTokenManager, Environment env) { + this.authenticationTokenManager = providerTokenManager; + this.env = env; + } @Override - public int getOrder(InvocationType invocationType, String microservice) { - return Filter.PRODUCER_SCHEDULE_FILTER_ORDER + 1010; + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER + 1010; } - @Nonnull @Override public String getName() { return "provider-public-key"; @@ -46,7 +55,12 @@ public String getName() { @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - String token = invocation.getContext(Const.AUTH_TOKEN); + String requestFullPath = SwaggerUtils.concatAbsolutePath(invocation.getSchemaMeta().getSwagger(), + invocation.getOperationMeta().getOperationPath()); + if (PathCheckUtils.isNotRequiredAuth(requestFullPath, env)) { + return nextNode.onFilter(invocation); + } + String token = invocation.getContext(CoreConst.AUTH_TOKEN); if (null != token && authenticationTokenManager.valid(token)) { return nextNode.onFilter(invocation); } diff --git a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderTokenManager.java b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderTokenManager.java index 0cf39a8b8f1..ebedacb7bf2 100644 --- a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderTokenManager.java +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderTokenManager.java @@ -24,11 +24,12 @@ import org.apache.servicecomb.authentication.RSAAuthenticationToken; import org.apache.servicecomb.foundation.common.utils.KeyPairUtils; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.cache.MicroserviceInstanceCache; +import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.apache.servicecomb.registry.discovery.MicroserviceInstanceCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import com.google.common.annotations.VisibleForTesting; import com.google.common.cache.Cache; @@ -42,7 +43,19 @@ public class ProviderTokenManager { .expireAfterAccess(getExpiredTime(), TimeUnit.MILLISECONDS) .build(); - private final AccessController accessController = new AccessController(); + private AccessController accessController; + + private MicroserviceInstanceCache microserviceInstanceCache; + + @Autowired + public void setMicroserviceInstanceCache(MicroserviceInstanceCache microserviceInstanceCache) { + this.microserviceInstanceCache = microserviceInstanceCache; + } + + @Autowired + public void setAccessController(AccessController accessController) { + this.accessController = accessController; + } public boolean valid(String token) { try { @@ -57,12 +70,14 @@ public boolean valid(String token) { } if (validatedToken.asMap().containsKey(rsaToken)) { - return accessController.isAllowed(MicroserviceInstanceCache.getOrCreate(rsaToken.getServiceId())); + return accessController.isAllowed(microserviceInstanceCache.getOrCreate( + rsaToken.getServiceId(), rsaToken.getInstanceId())); } if (isValidToken(rsaToken) && !tokenExpired(rsaToken)) { validatedToken.put(rsaToken, true); - return accessController.isAllowed(MicroserviceInstanceCache.getOrCreate(rsaToken.getServiceId())); + return accessController.isAllowed(microserviceInstanceCache.getOrCreate( + rsaToken.getServiceId(), rsaToken.getInstanceId())); } return false; } catch (InvalidKeyException | NoSuchAlgorithmException | InvalidKeySpecException | SignatureException e) { @@ -91,7 +106,7 @@ private boolean tokenExpired(RSAAuthenticationToken rsaToken) { } private String getPublicKeyFromInstance(String instanceId, String serviceId) { - MicroserviceInstance instances = MicroserviceInstanceCache.getOrCreate(serviceId, instanceId); + DiscoveryInstance instances = microserviceInstanceCache.getOrCreate(serviceId, instanceId); if (instances != null) { return instances.getProperties().get(DefinitionConst.INSTANCE_PUBKEY_PRO); } else { diff --git a/handlers/handler-publickey-auth/src/main/resources/META-INF/spring.factories b/handlers/handler-publickey-auth/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 6530ebedd14..00000000000 --- a/handlers/handler-publickey-auth/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,19 +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. -## --------------------------------------------------------------------------- - -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.authentication.AuthenticationConfiguration diff --git a/handlers/handler-publickey-auth/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/handlers/handler-publickey-auth/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..23589733acf --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.authentication.AuthenticationConfiguration diff --git a/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestAccessController.java b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestAccessController.java index 059ac411a73..0c6c6afd346 100644 --- a/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestAccessController.java +++ b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestAccessController.java @@ -20,144 +20,259 @@ import java.util.Map; import org.apache.servicecomb.authentication.provider.AccessController; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.junit.jupiter.api.AfterEach; +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.MutablePropertySources; public class TestAccessController { - @AfterEach + ConfigurableEnvironment environment; + + EnumerablePropertySource propertySource; + + @BeforeEach public void tearDown() { - ArchaiusUtils.resetConfig(); + environment = Mockito.mock(ConfigurableEnvironment.class); + propertySource = Mockito.mock(EnumerablePropertySource.class); } @Test public void testIsValidOfWhiteByServiceName() { - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.propertyName", "serviceName"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.category", "property"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.rule", "trust*"); - AccessController controller = new AccessController(); - Microservice service = new Microservice(); + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + mutablePropertySources.addLast(propertySource); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); + Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { + "servicecomb.publicKey.accessControl.white.list1.propertyName", + "servicecomb.publicKey.accessControl.white.list1.category", + "servicecomb.publicKey.accessControl.white.list1.rule" + }); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.propertyName")) + .thenReturn("serviceName"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.category")) + .thenReturn("property"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.rule")) + .thenReturn("trust*"); - service.setServiceName("trustCustomer"); + AccessController controller = new AccessController(environment); + DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("trustCustomer"); Assertions.assertTrue(controller.isAllowed(service)); - service.setServiceName("nottrustCustomer"); + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("nottrustCustomer"); Assertions.assertFalse(controller.isAllowed(service)); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.rule", "*trust"); - service.setServiceName("Customer_trust"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.rule")) + .thenReturn("*trust"); + Map latest = new HashMap<>(); + latest.put("servicecomb.publicKey.accessControl.white.list1.rule", "*trust"); + controller.onConfigurationChangedEvent(ConfigurationChangedEvent.createIncremental(latest, new HashMap<>())); + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("Customer_trust"); Assertions.assertTrue(controller.isAllowed(service)); - service.setServiceName("Customer_trust_not"); + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("Customer_trust_not"); Assertions.assertFalse(controller.isAllowed(service)); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.rule", "trust"); - service.setServiceName("trust"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.rule")) + .thenReturn("trust"); + latest.put("servicecomb.publicKey.accessControl.white.list1.rule", "trust"); + controller.onConfigurationChangedEvent(ConfigurationChangedEvent.createIncremental(latest, new HashMap<>())); + + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("trust"); Assertions.assertTrue(controller.isAllowed(service)); - service.setServiceName("Customer_trust"); + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("Customer_trust"); Assertions.assertFalse(controller.isAllowed(service)); } @Test public void testIsValidOfBlackByServiceName() { - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.propertyName", "serviceName"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.category", "property"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.rule", "trust*"); - AccessController controller = new AccessController(); - Microservice service = new Microservice(); + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + mutablePropertySources.addLast(propertySource); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); + Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { + "servicecomb.publicKey.accessControl.black.list1.propertyName", + "servicecomb.publicKey.accessControl.black.list1.category", + "servicecomb.publicKey.accessControl.black.list1.rule" + }); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.propertyName")) + .thenReturn("serviceName"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.category")) + .thenReturn("property"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.rule")) + .thenReturn("trust*"); - service.setServiceName("trustCustomer"); + AccessController controller = new AccessController(environment); + + DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("trustCustomer"); Assertions.assertFalse(controller.isAllowed(service)); - service.setServiceName("nottrustCustomer"); + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("nottrustCustomer"); Assertions.assertTrue(controller.isAllowed(service)); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.rule", "*trust"); - service.setServiceName("Customer_trust"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.rule")) + .thenReturn("*trust"); + Map latest = new HashMap<>(); + latest.put("servicecomb.publicKey.accessControl.black.list1.rule", "*trust"); + controller.onConfigurationChangedEvent(ConfigurationChangedEvent.createIncremental(latest, new HashMap<>())); + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("Customer_trust"); Assertions.assertFalse(controller.isAllowed(service)); - service.setServiceName("Customer_trust_not"); + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("Customer_trust_not"); Assertions.assertTrue(controller.isAllowed(service)); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.rule", "trust"); - service.setServiceName("trust"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.rule")) + .thenReturn("trust"); + latest = new HashMap<>(); + latest.put("servicecomb.publicKey.accessControl.black.list1.rule", "trust"); + controller.onConfigurationChangedEvent(ConfigurationChangedEvent.createIncremental(latest, new HashMap<>())); + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("trust"); Assertions.assertFalse(controller.isAllowed(service)); - service.setServiceName("Customer_trust"); + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("Customer_trust"); Assertions.assertTrue(controller.isAllowed(service)); } @Test public void testIsValidOfBlackAndWhiteByServiceName() { - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.propertyName", "serviceName"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.category", "property"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.rule", "trust*"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.propertyName", "serviceName"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.category", "property"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.rule", "*hacker"); - - AccessController controller = new AccessController(); - Microservice service = new Microservice(); + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + mutablePropertySources.addLast(propertySource); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); + Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { + "servicecomb.publicKey.accessControl.black.list1.propertyName", + "servicecomb.publicKey.accessControl.black.list1.category", + "servicecomb.publicKey.accessControl.black.list1.rule", + "servicecomb.publicKey.accessControl.white.list1.propertyName", + "servicecomb.publicKey.accessControl.white.list1.category", + "servicecomb.publicKey.accessControl.white.list1.rule" + }); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.propertyName")) + .thenReturn("serviceName"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.category")) + .thenReturn("property"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.rule")) + .thenReturn("trust*"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.propertyName")) + .thenReturn("serviceName"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.category")) + .thenReturn("property"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.rule")) + .thenReturn("*hacker"); - service.setServiceName("trustCustomer"); + AccessController controller = new AccessController(environment); + DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("trustCustomer"); Assertions.assertTrue(controller.isAllowed(service)); - service.setServiceName("trustCustomerhacker"); + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("trustCustomerhacker"); Assertions.assertFalse(controller.isAllowed(service)); } @Test public void testIsValidOfBlackByProperties() { - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.propertyName", "tag"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.category", "property"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.rule", "test"); - AccessController controller = new AccessController(); - Microservice service = new Microservice(); + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + mutablePropertySources.addLast(propertySource); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); + Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { + "servicecomb.publicKey.accessControl.black.list1.propertyName", + "servicecomb.publicKey.accessControl.black.list1.category", + "servicecomb.publicKey.accessControl.black.list1.rule", + }); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.propertyName")) + .thenReturn("tag"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.category")) + .thenReturn("property"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.rule")) + .thenReturn("test"); + + AccessController controller = new AccessController(environment); + DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); Map map = new HashMap<>(); map.put("tag", "test"); + Mockito.when(service.getProperties()).thenReturn(map); - service.setProperties(map); Assertions.assertFalse(controller.isAllowed(service)); map.put("tag", "testa"); - service.setProperties(map); + Mockito.when(service.getProperties()).thenReturn(map); Assertions.assertTrue(controller.isAllowed(service)); } @Test public void testIsValidOfWhiteByProperties() { - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.propertyName", "tag"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.category", "property"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.rule", "test"); - AccessController controller = new AccessController(); - Microservice service = new Microservice(); + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + mutablePropertySources.addLast(propertySource); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); + Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { + "servicecomb.publicKey.accessControl.white.list1.propertyName", + "servicecomb.publicKey.accessControl.white.list1.category", + "servicecomb.publicKey.accessControl.white.list1.rule", + }); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.propertyName")) + .thenReturn("tag"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.category")) + .thenReturn("property"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.rule")) + .thenReturn("test"); + + AccessController controller = new AccessController(environment); + DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); Map map = new HashMap<>(); map.put("tag", "test"); - - service.setProperties(map); + Mockito.when(service.getProperties()).thenReturn(map); Assertions.assertTrue(controller.isAllowed(service)); map.put("tag", "testa"); - service.setProperties(map); + Mockito.when(service.getProperties()).thenReturn(map); Assertions.assertFalse(controller.isAllowed(service)); } @Test public void testIsValidOfBlackAndWhiteByServiceNameAndVersion() { - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.propertyName", "serviceName"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.category", "property"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.white.list1.rule", "trust*"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.propertyName", "version"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.category", "property"); - ArchaiusUtils.setProperty("servicecomb.publicKey.accessControl.black.list1.rule", "0.0.1"); - - AccessController controller = new AccessController(); - Microservice service = new Microservice(); - service.setServiceName("trustCustomer"); - service.setVersion("0.0.1"); + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + mutablePropertySources.addLast(propertySource); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); + Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { + "servicecomb.publicKey.accessControl.black.list1.propertyName", + "servicecomb.publicKey.accessControl.black.list1.category", + "servicecomb.publicKey.accessControl.black.list1.rule", + "servicecomb.publicKey.accessControl.white.list1.propertyName", + "servicecomb.publicKey.accessControl.white.list1.category", + "servicecomb.publicKey.accessControl.white.list1.rule" + }); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.propertyName")) + .thenReturn("serviceName"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.category")) + .thenReturn("property"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.rule")) + .thenReturn("trust*"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.propertyName")) + .thenReturn("version"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.category")) + .thenReturn("property"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.rule")) + .thenReturn("0.0.1"); + + AccessController controller = new AccessController(environment); + DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("trustCustomer"); + Mockito.when(service.getVersion()).thenReturn("0.0.1"); Assertions.assertFalse(controller.isAllowed(service)); } diff --git a/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestAuthenticationBootListener.java b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestAuthenticationBootListener.java index ef44ff462e6..28cb8d48d2d 100644 --- a/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestAuthenticationBootListener.java +++ b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestAuthenticationBootListener.java @@ -16,44 +16,66 @@ */ package org.apache.servicecomb.authentication; -import org.apache.servicecomb.config.ConfigUtil; +import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; +import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; + +import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.BootListener.BootEvent; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.token.Keypair4Auth; import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; import org.apache.servicecomb.registry.definition.DefinitionConst; 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 org.mockito.Mockito; +import org.springframework.core.env.Environment; public class TestAuthenticationBootListener { private SCBEngine engine; + private Environment environment; + @BeforeEach public void setUp() { - ConfigUtil.installDynamicConfig(); - engine = SCBBootstrap.createSCBEngineForTest().run(); + environment = Mockito.mock(Environment.class); + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keyGeneratorAlgorithm", "RSA")) + .thenReturn("RSA"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.signAlgorithm", "SHA256withRSA")) + .thenReturn("SHA256withRSA"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keySize", int.class, 2048)) + .thenReturn(2048); + Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)) + .thenReturn(BootStrapProperties.DEFAULT_APPLICATION); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)) + .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_NAME); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_ENVIRONMENT)) + .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_ENVIRONMENT); + + engine = SCBBootstrap.createSCBEngineForTest(environment); + engine.run(); } @AfterEach public void teardown() { engine.destroy(); - ArchaiusUtils.resetConfig(); } @Test public void testGenerateRSAKey() { - MicroserviceInstance microserviceInstance = new MicroserviceInstance(); - Microservice microservice = new Microservice(); - microservice.setInstance(microserviceInstance); - + RegistrationManager registrationManager = Mockito.mock(RegistrationManager.class); AuthenticationBootListener authenticationBootListener = new AuthenticationBootListener(); + authenticationBootListener.setRegistrationManager(registrationManager); BootEvent bootEvent = new BootEvent(); bootEvent.setEventType(BootListener.EventType.BEFORE_REGISTRY); authenticationBootListener.onBootEvent(bootEvent); @@ -63,12 +85,15 @@ public void testGenerateRSAKey() { @Test public void testMicroserviceInstancePublicKey() { + RegistrationManager registrationManager = Mockito.mock(RegistrationManager.class); AuthenticationBootListener authenticationBootListener = new AuthenticationBootListener(); + authenticationBootListener.setRegistrationManager(registrationManager); BootEvent bootEvent = new BootEvent(); bootEvent.setEventType(BootListener.EventType.BEFORE_REGISTRY); + authenticationBootListener.onBootEvent(bootEvent); - String publicKey = RegistrationManager.INSTANCE.getMicroserviceInstance(). - getProperties().get(DefinitionConst.INSTANCE_PUBKEY_PRO); - Assertions.assertNotNull(publicKey); + + Mockito.verify(registrationManager, times(1)) + .addProperty(eq(DefinitionConst.INSTANCE_PUBKEY_PRO), any(String.class)); } } diff --git a/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestRSAAuthenticationToken.java b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestRSAAuthenticationToken.java index 586e8352125..e9ec7e2b2a9 100644 --- a/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestRSAAuthenticationToken.java +++ b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestRSAAuthenticationToken.java @@ -16,18 +16,38 @@ */ package org.apache.servicecomb.authentication; +import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; +import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; + import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.KeyPairUtils; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; public class TestRSAAuthenticationToken { + static Environment environment = Mockito.mock(Environment.class); + @BeforeAll + public static void setUpClass() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keyGeneratorAlgorithm", "RSA")) + .thenReturn("RSA"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.signAlgorithm", "SHA256withRSA")) + .thenReturn("SHA256withRSA"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keySize", int.class, 2048)) + .thenReturn(2048); + Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); + } @Test public void testRSAAuthenticationToken() diff --git a/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestPathCheckUtils.java b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestPathCheckUtils.java new file mode 100644 index 00000000000..b8ff4623081 --- /dev/null +++ b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestPathCheckUtils.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.authentication.provider; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.when; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.servers.Server; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class TestPathCheckUtils { + private static final String KEY_INCLUDE_PATH = "servicecomb.publicKey.accessControl.includePathPatterns"; + + private static final String KEY_EXCLUDE_PATH = "servicecomb.publicKey.accessControl.excludePathPatterns"; + + private static final String BASE_PATH = "/api/v1"; + + private Environment environment; + + private OpenAPI swagger; + + @BeforeEach + public void setUp() { + environment = Mockito.mock(Environment.class); + swagger = new OpenAPI(); + swagger.setServers(new ArrayList<>()); + swagger.getServers().add(new Server().url(BASE_PATH)); + } + + @Test + public void testExcludePathWithBasePathAndExactMatch() { + String operationPath = "/public"; + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(BASE_PATH + operationPath); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, operationPath); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for excluded path with exact match"); + } + + @Test + public void testExcludePathWithBasePathAndWildcard() { + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(BASE_PATH + "/public/*"); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public/test"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for excluded path with wildcard"); + + fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public/nested/path"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for excluded nested path with wildcard"); + } + + @Test + public void testIncludePathWithBasePath() { + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(""); + when(environment.getProperty(KEY_INCLUDE_PATH, "")).thenReturn(BASE_PATH + "/private/*"); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/private/resource"); + assertFalse(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should require auth for included path"); + + fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public/resource"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for non-included path"); + } + + @Test + public void testExcludeOverrideIncludePath() { + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(BASE_PATH + "/resource"); + when(environment.getProperty(KEY_INCLUDE_PATH, "")).thenReturn(BASE_PATH + "/*"); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/resource"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Exclude patterns should override include patterns"); + } + + @Test + public void testMultipleExcludePaths() { + List operationPaths = List.of("/public", "/health", "/metrics/*"); + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(concatPath(BASE_PATH, operationPaths)); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for first exclude path"); + + fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/health"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for second exclude path"); + + fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/metrics/jvm"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for wildcard exclude path"); + } + + @Test + public void testDifferentBasePath() { + String basePath = "/different/base"; + String publicOperationPath = "/public"; + String privateOperationPath = "/private"; + swagger.getServers().clear(); + swagger.getServers().add(new Server().url(basePath)); + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(basePath + publicOperationPath); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, publicOperationPath); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth with different base path"); + + fullPath = SwaggerUtils.concatAbsolutePath(swagger, privateOperationPath); + assertFalse(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should require auth for non-excluded path with different base path"); + } + + @Test + public void testNoBasePath() { + String operationPath = "/public"; + swagger.setServers(null); + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(operationPath); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, operationPath); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth when no base path is set"); + } + + @Test + public void testEmptyConfiguration() { + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(""); + when(environment.getProperty(KEY_INCLUDE_PATH, "")).thenReturn(""); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/any/path"); + assertFalse(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should require auth by default when no patterns are configured"); + } + + private String concatPath(String basePath, List paths) { + return paths.stream().map(path -> basePath + path).collect(Collectors.joining(",")); + } +} diff --git a/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestProviderTokenManager.java b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestProviderTokenManager.java index 6b143de0165..801f91e697d 100644 --- a/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestProviderTokenManager.java +++ b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestProviderTokenManager.java @@ -16,42 +16,58 @@ */ package org.apache.servicecomb.authentication.provider; +import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; +import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; +import static org.mockito.ArgumentMatchers.any; + import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.authentication.RSAAuthenticationToken; import org.apache.servicecomb.authentication.consumer.ConsumerTokenManager; -import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.KeyPairEntry; import org.apache.servicecomb.foundation.common.utils.KeyPairUtils; -import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.apache.servicecomb.foundation.token.Keypair4Auth; -import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.registry.cache.MicroserviceInstanceCache; +import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.definition.DefinitionConst; - -import com.google.common.cache.Cache; - +import org.apache.servicecomb.registry.discovery.MicroserviceInstanceCache; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MutablePropertySources; + +import com.google.common.cache.Cache; public class TestProviderTokenManager { + static Environment environment = Mockito.mock(Environment.class); + @BeforeAll + public static void setUpClass() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keyGeneratorAlgorithm", "RSA")) + .thenReturn("RSA"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.signAlgorithm", "SHA256withRSA")) + .thenReturn("SHA256withRSA"); + Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keySize", int.class, 2048)) + .thenReturn(2048); + Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); + } @BeforeEach public void setUp() { - ConfigUtil.installDynamicConfig(); } @AfterEach public void teardown() { - ArchaiusUtils.resetConfig(); } @Test @@ -59,9 +75,9 @@ public void testTokenExpired() { String tokenStr = "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; ProviderTokenManager tokenManager = new ProviderTokenManager(); - MicroserviceInstance microserviceInstance = new MicroserviceInstance(); + DiscoveryInstance microserviceInstance = Mockito.mock(DiscoveryInstance.class); Map properties = new HashMap<>(); - microserviceInstance.setProperties(properties); + Mockito.when(microserviceInstance.getProperties()).thenReturn(properties); properties.put(DefinitionConst.INSTANCE_PUBKEY_PRO, "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxKl5TNUTec7fL2degQcCk6vKf3c0wsfNK5V6elKzjWxm0MwbRj/UeR20VSnicBmVIOWrBS9LiERPPvjmmWUOSS2vxwr5XfhBhZ07gCAUNxBOTzgMo5nE45DhhZu5Jzt5qSV6o10Kq7+fCCBlDZ1UoWxZceHkUt5AxcrhEDulFjQIDAQAB"); Assertions.assertFalse(tokenManager.valid(tokenStr)); @@ -80,15 +96,27 @@ protected int getExpiredTime() { return 500; } }); - - try (MockedStatic rsaAuthenticationTokenMockedStatic = Mockito.mockStatic(RSAAuthenticationToken.class)) { + ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); + tokenManager.setAccessController(new AccessController(environment)); + MicroserviceInstanceCache microserviceInstanceCache = Mockito.mock(MicroserviceInstanceCache.class); + DiscoveryInstance microserviceInstance = Mockito.mock(DiscoveryInstance.class); + Mockito.when(microserviceInstance.getInstanceId()).thenReturn(""); + Map properties = new HashMap<>(); + Mockito.when(microserviceInstance.getProperties()).thenReturn(properties); + Mockito.when(microserviceInstanceCache.getOrCreate(any(String.class), any(String.class))) + .thenReturn(microserviceInstance); + tokenManager.setMicroserviceInstanceCache(microserviceInstanceCache); + try (MockedStatic rsaAuthenticationTokenMockedStatic = Mockito.mockStatic( + RSAAuthenticationToken.class)) { rsaAuthenticationTokenMockedStatic.when(() -> RSAAuthenticationToken.fromStr(tokenStr)).thenReturn(token); Mockito.when(token.getGenerateTime()).thenReturn(System.currentTimeMillis()); Mockito.doReturn(true).when(tokenManager).isValidToken(token); Assertions.assertTrue(tokenManager.valid(tokenStr)); Cache cache = tokenManager - .getValidatedToken(); + .getValidatedToken(); Assertions.assertTrue(cache.asMap().containsKey(token)); Thread.sleep(1000); @@ -102,34 +130,36 @@ public void testTokenFromValidatePool() { Keypair4Auth.INSTANCE.setPrivateKey(keyPairEntry.getPrivateKey()); Keypair4Auth.INSTANCE.setPublicKey(keyPairEntry.getPublicKey()); Keypair4Auth.INSTANCE.setPublicKeyEncoded(keyPairEntry.getPublicKeyEncoded()); - String serviceId = "c8636e5acf1f11e7b701286ed488fc20"; - String instanceId = "e8a04b54cf2711e7b701286ed488fc20"; + String serviceId = "test"; + String instanceId = "test"; ConsumerTokenManager consumerTokenManager = new ConsumerTokenManager(); - MicroserviceInstance microserviceInstance = new MicroserviceInstance(); - microserviceInstance.setInstanceId(instanceId); + + ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)).thenReturn("test"); + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)).thenReturn("test"); + consumerTokenManager.setEnvironment(environment); + DiscoveryInstance microserviceInstance = Mockito.mock(DiscoveryInstance.class); + Mockito.when(microserviceInstance.getInstanceId()).thenReturn(instanceId); Map properties = new HashMap<>(); - microserviceInstance.setProperties(properties); + Mockito.when(microserviceInstance.getProperties()).thenReturn(properties); properties.put(DefinitionConst.INSTANCE_PUBKEY_PRO, keyPairEntry.getPublicKeyEncoded()); - Microservice microservice = new Microservice(); - microservice.setServiceId(serviceId); - RegistrationManager.INSTANCE = Mockito.spy(RegistrationManager.INSTANCE); - Mockito.when(RegistrationManager.INSTANCE.getMicroservice()).thenReturn(microservice); - Mockito.when(RegistrationManager.INSTANCE.getMicroserviceInstance()).thenReturn(microserviceInstance); + MicroserviceInstanceCache microserviceInstanceCache = Mockito.mock(MicroserviceInstanceCache.class); + Mockito.when(microserviceInstanceCache.getOrCreate(serviceId, instanceId)).thenReturn(microserviceInstance); //Test Consumer first create token String token = consumerTokenManager.getToken(); Assertions.assertNotNull(token); // use cache token Assertions.assertEquals(token, consumerTokenManager.getToken()); - try (MockedStatic microserviceInstanceCacheMockedStatic = Mockito.mockStatic(MicroserviceInstanceCache.class)) { - microserviceInstanceCacheMockedStatic.when(() -> MicroserviceInstanceCache.getOrCreate(serviceId, instanceId)) - .thenReturn(microserviceInstance); - microserviceInstanceCacheMockedStatic.when(() -> MicroserviceInstanceCache.getOrCreate(serviceId)) - .thenReturn(microservice); - ProviderTokenManager rsaProviderTokenManager = new ProviderTokenManager(); - //first validate need to verify use RSA - Assertions.assertTrue(rsaProviderTokenManager.valid(token)); - // second validate use validated pool - Assertions.assertTrue(rsaProviderTokenManager.valid(token)); - } + ProviderTokenManager rsaProviderTokenManager = new ProviderTokenManager(); + + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); + rsaProviderTokenManager.setAccessController(new AccessController(environment)); + + rsaProviderTokenManager.setMicroserviceInstanceCache(microserviceInstanceCache); + //first validate need to verify use RSA + Assertions.assertTrue(rsaProviderTokenManager.valid(token)); + // second validate use validated pool + Assertions.assertTrue(rsaProviderTokenManager.valid(token)); } } diff --git a/handlers/handler-publickey-auth/src/test/resources/microservice.yaml b/handlers/handler-publickey-auth/src/test/resources/microservice.yaml index 4db1321ba99..5ff7ed9ae41 100644 --- a/handlers/handler-publickey-auth/src/test/resources/microservice.yaml +++ b/handlers/handler-publickey-auth/src/test/resources/microservice.yaml @@ -20,4 +20,4 @@ servicecomb: consumer: default: empty producer: - default: empty \ No newline at end of file + default: empty diff --git a/handlers/handler-router/pom.xml b/handlers/handler-router/pom.xml index ce9ae80b426..0068002eb7d 100644 --- a/handlers/handler-router/pom.xml +++ b/handlers/handler-router/pom.xml @@ -22,7 +22,7 @@ handlers org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -30,10 +30,6 @@ Java Chassis::Handlers::Router - - com.netflix.ribbon - ribbon-loadbalancer - org.springframework spring-core @@ -63,4 +59,4 @@ handler-loadbalance - \ No newline at end of file + diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/MicroserviceCache.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/MicroserviceCache.java deleted file mode 100644 index a3edb9713ab..00000000000 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/MicroserviceCache.java +++ /dev/null @@ -1,43 +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.servicecomb.router.custom; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.registry.api.registry.Microservice; - -public final class MicroserviceCache { - - private static final MicroserviceCache instance = new MicroserviceCache(); - - private final Map services = new HashMap<>(); - - private MicroserviceCache() { - } - - public static MicroserviceCache getInstance() { - return instance; - } - - public Microservice getService(String serviceId) { - return services.computeIfAbsent(serviceId, (k) -> - DiscoveryManager.INSTANCE.getMicroservice(serviceId) - ); - } -} diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterHeaderFilterExt.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterHeaderFilterExt.java deleted file mode 100644 index 7951850f54a..00000000000 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterHeaderFilterExt.java +++ /dev/null @@ -1,35 +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.servicecomb.router.custom; - -import java.util.Map; - -/** - * allow users to filter not necessary headers in request. - **/ -public interface RouterHeaderFilterExt { - - default int getOrder() { - return 0; - } - - default boolean enabled() { - return true; - } - - Map doFilter(Map invokeHeader); -} diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterInvokeFilter.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterInvokeFilter.java deleted file mode 100644 index b40bf543dc8..00000000000 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterInvokeFilter.java +++ /dev/null @@ -1,132 +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.servicecomb.router.custom; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.common.rest.filter.HttpServerFilter; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.common.utils.JsonUtils; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.swagger.invocation.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.CollectionUtils; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicStringProperty; - -public class RouterInvokeFilter implements HttpServerFilter { - - private static final Logger LOGGER = LoggerFactory.getLogger(RouterInvokeFilter.class); - - private static final String SERVICECOMB_ROUTER_HEADER = "servicecomb.router.header"; - - private static List allHeader = new ArrayList<>(); - - - @Override - public int getOrder() { - return -90; - } - - @Override - public boolean enabled() { - return true; - } - - @Override - public Response afterReceiveRequest(Invocation invocation, - HttpServletRequestEx httpServletRequestEx) { - if (!StringUtils.isEmpty(invocation.getContext(RouterServerListFilter.ROUTER_HEADER))) { - return null; - } - - if (!isHaveHeadersRule()) { - return null; - } - if (loadHeaders()) { - Map headerMap = getHeaderMap(httpServletRequestEx); - try { - invocation - .addContext(RouterServerListFilter.ROUTER_HEADER, JsonUtils.OBJ_MAPPER.writeValueAsString(headerMap)); - } catch (JsonProcessingException e) { - LOGGER.error("canary context serialization failed"); - } - } - return null; - } - - /** - * read config and get Header - */ - private boolean loadHeaders() { - if (!CollectionUtils.isEmpty(allHeader)) { - return true; - } - DynamicStringProperty headerStr = DynamicPropertyFactory.getInstance() - .getStringProperty(SERVICECOMB_ROUTER_HEADER, null, () -> { - DynamicStringProperty temHeader = DynamicPropertyFactory.getInstance() - .getStringProperty(SERVICECOMB_ROUTER_HEADER, null); - if (!addAllHeaders(temHeader.get())) { - allHeader = new ArrayList<>(); - } - }); - return addAllHeaders(headerStr.get()); - } - - /** - * if don't have headers rule , avoid registered too many callback - */ - private boolean isHaveHeadersRule() { - DynamicStringProperty headerStr = DynamicPropertyFactory.getInstance() - .getStringProperty(SERVICECOMB_ROUTER_HEADER, null); - return StringUtils.isNotEmpty(headerStr.get()); - } - - private boolean addAllHeaders(String str) { - if (StringUtils.isEmpty(str)) { - return false; - } - try { - if (CollectionUtils.isEmpty(allHeader)) { - allHeader = Arrays.asList(str.split(",")); - } - } catch (Exception e) { - LOGGER.error("route management Serialization failed: {}", e.getMessage()); - return false; - } - return true; - } - - private Map getHeaderMap(HttpServletRequestEx httpServletRequestEx) { - Map headerMap = new HashMap<>(); - allHeader.forEach(headerKey -> { - String val = httpServletRequestEx.getHeader(headerKey); - if (!StringUtils.isEmpty(val)) { - headerMap.put(headerKey, httpServletRequestEx.getHeader(headerKey)); - } - }); - return headerMap; - } -} diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.java index 0e5ae7d095b..fdae82e1445 100644 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.java +++ b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.java @@ -16,45 +16,32 @@ */ package org.apache.servicecomb.router.custom; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.governance.MatchType; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.foundation.common.utils.JsonUtils; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.loadbalance.ServerListFilterExt; import org.apache.servicecomb.loadbalance.ServiceCombServer; -import org.apache.servicecomb.registry.api.registry.Microservice; import org.apache.servicecomb.router.RouterFilter; import org.apache.servicecomb.router.distribute.RouterDistributor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.netflix.config.DynamicPropertyFactory; public class RouterServerListFilter implements ServerListFilterExt { - private static final Logger LOGGER = LoggerFactory.getLogger(RouterServerListFilter.class); - private static final String ENABLE = "servicecomb.router.type"; private static final String TYPE_ROUTER = "router"; - public static final String ROUTER_HEADER = "X-RouterContext"; - @SuppressWarnings("unchecked") - private final RouterDistributor routerDistributor = BeanUtils + private final RouterDistributor routerDistributor = BeanUtils .getBean(RouterDistributor.class); private final RouterFilter routerFilter = BeanUtils.getBean(RouterFilter.class); @Override public boolean enabled() { - return DynamicPropertyFactory.getInstance().getStringProperty(ENABLE, "").get() + return LegacyPropertyFactory.getStringProperty(ENABLE, "") .equals(TYPE_ROUTER); } @@ -62,42 +49,7 @@ public boolean enabled() { public List getFilteredListOfServers(List list, Invocation invocation) { String targetServiceName = invocation.getMicroserviceName(); - Map headers = addHeaders(invocation); - headers = filterHeaders(headers); - return routerFilter - .getFilteredListOfServers(list, targetServiceName, headers, - routerDistributor); - } - - private Map filterHeaders(Map headers) { - List filters = SPIServiceUtils - .getOrLoadSortedService(RouterHeaderFilterExt.class); - for (RouterHeaderFilterExt filterExt : filters) { - if (filterExt.enabled()) { - headers = filterExt.doFilter(headers); - } - } - return headers; - } - - private Map addHeaders(Invocation invocation) { - Map headers = new HashMap<>(); - if (invocation.getContext(ROUTER_HEADER) != null) { - Map canaryContext = null; - try { - canaryContext = JsonUtils.OBJ_MAPPER - .readValue(invocation.getContext(ROUTER_HEADER), - new TypeReference>() { - }); - } catch (JsonProcessingException e) { - LOGGER.error("canary context serialization failed"); - } - if (canaryContext != null) { - headers.putAll(canaryContext); - } - } - invocation.getInvocationArguments().forEach((k, v) -> headers.put(k, v == null ? null : v.toString())); - headers.putAll(invocation.getContext()); - return headers; + return routerFilter.getFilteredListOfServers(list, targetServiceName, + MatchType.createGovHttpRequest(invocation), routerDistributor); } } diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombCanaryDistributer.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombCanaryDistributer.java deleted file mode 100644 index cde6807f41d..00000000000 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombCanaryDistributer.java +++ /dev/null @@ -1,33 +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.servicecomb.router.custom; - -import org.apache.servicecomb.loadbalance.ServiceCombServer; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.router.distribute.AbstractRouterDistributor; - -public class ServiceCombCanaryDistributer extends - AbstractRouterDistributor { - - public ServiceCombCanaryDistributer() { - init(server -> MicroserviceCache.getInstance() - .getService(server.getInstance().getServiceId()), - Microservice::getVersion, - Microservice::getServiceName, - Microservice::getProperties); - } -} diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.java index 9e8a5a3faa0..ff6952d24c8 100644 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.java +++ b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.java @@ -29,7 +29,7 @@ public class ServiceCombRouterConfiguration { public static final String ROUTER_ENABLED = ROUTER_PREFIX + ".enabled"; @Bean - public ServiceCombCanaryDistributer serviceCombCanaryDistributer() { - return new ServiceCombCanaryDistributer(); + public ServiceCombRouterDistributor scbServiceCombRouterDistributor() { + return new ServiceCombRouterDistributor(); } } diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterDistributor.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterDistributor.java new file mode 100644 index 00000000000..709e4598bd8 --- /dev/null +++ b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterDistributor.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.servicecomb.router.custom; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.loadbalance.ServiceCombServer; +import org.apache.servicecomb.router.distribute.AbstractRouterDistributor; + +public class ServiceCombRouterDistributor extends + AbstractRouterDistributor { + + public ServiceCombRouterDistributor() { + init( + instance -> instance.getInstance().getVersion(), + instance -> instance.getInstance().getServiceName(), + instance -> { + Map properties = new HashMap<>(); + properties.putAll(instance.getInstance().getProperties()); + return properties; + }); + } +} diff --git a/handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter b/handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter deleted file mode 100644 index 02a728b1987..00000000000 --- a/handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.filter.HttpServerFilter +++ /dev/null @@ -1,18 +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. -# - -org.apache.servicecomb.router.custom.RouterInvokeFilter \ No newline at end of file diff --git a/handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt b/handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt index f5a6ffa008e..0e6f5d9a65b 100644 --- a/handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt +++ b/handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.router.custom.RouterServerListFilter \ No newline at end of file +org.apache.servicecomb.router.custom.RouterServerListFilter diff --git a/handlers/handler-router/src/main/resources/META-INF/spring.factories b/handlers/handler-router/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 47ff945b190..00000000000 --- a/handlers/handler-router/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,18 +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. -# -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.router.custom.ServiceCombRouterConfiguration diff --git a/handlers/handler-router/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/handlers/handler-router/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..8bbbb93c1f5 --- /dev/null +++ b/handlers/handler-router/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.router.custom.ServiceCombRouterConfiguration diff --git a/handlers/handler-tracing-zipkin/pom.xml b/handlers/handler-tracing-zipkin/pom.xml index eb287f25931..3644d209bab 100644 --- a/handlers/handler-tracing-zipkin/pom.xml +++ b/handlers/handler-tracing-zipkin/pom.xml @@ -22,7 +22,7 @@ handlers org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -50,10 +50,6 @@ io.zipkin.brave brave - - io.zipkin.reporter2 - zipkin-sender-okhttp3 - io.zipkin.brave brave-context-slf4j @@ -61,7 +57,14 @@ io.zipkin.zipkin2 zipkin - compile + + + io.zipkin.reporter2 + zipkin-sender-okhttp3 + + + io.zipkin.reporter2 + zipkin-reporter-brave diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpRequestParser.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpRequestParser.java new file mode 100644 index 00000000000..32fb8752028 --- /dev/null +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpRequestParser.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.servicecomb.tracing.zipkin; + +import org.apache.servicecomb.core.Invocation; + +import brave.SpanCustomizer; +import brave.http.HttpRequest; +import brave.http.HttpRequestParser; +import brave.http.HttpTags; +import brave.propagation.TraceContext; + +public record CustomHttpRequestParser() implements HttpRequestParser { + @Override + public void parse(HttpRequest req, TraceContext context, SpanCustomizer span) { + Invocation invocation = ((InvocationAware) req).getInvocation(); + span.name(invocation.getInvocationQualifiedName()); + HttpTags.METHOD.tag(req, context, span); + HttpTags.PATH.tag(req, context, span); + if (!invocation.isConsumer()) { // server or edge + HttpTags.ROUTE.tag(req, context, span); + } + } +} diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpResponseParser.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpResponseParser.java new file mode 100644 index 00000000000..bb59f7e9371 --- /dev/null +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpResponseParser.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.tracing.zipkin; + +import org.apache.servicecomb.core.Invocation; + +import brave.SpanCustomizer; +import brave.Tags; +import brave.http.HttpResponse; +import brave.http.HttpResponseParser; +import brave.http.HttpTags; +import brave.propagation.TraceContext; + +public record CustomHttpResponseParser() implements HttpResponseParser { + @Override + public void parse(HttpResponse response, TraceContext context, SpanCustomizer span) { + Invocation invocation = ((InvocationAware) response).getInvocation(); + HttpTags.STATUS_CODE.tag(response, context, span); + if (response.error() != null) { + span.tag(Tags.ERROR.key(), response.error().getMessage()); + } + if (!invocation.isProducer()) { // client or edge + span.tag(HttpTags.ROUTE.key(), response.route()); + } + } +} diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientRequestWrapper.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientRequestWrapper.java index a8196907673..cc758d68a27 100644 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientRequestWrapper.java +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientRequestWrapper.java @@ -17,27 +17,17 @@ package org.apache.servicecomb.tracing.zipkin; -import brave.http.HttpClientRequest; - import org.apache.servicecomb.core.Invocation; -class HttpClientRequestWrapper extends HttpClientRequest { - - private Invocation invocation; +import brave.http.HttpClientRequest; - HttpClientRequestWrapper() { - } +class HttpClientRequestWrapper extends HttpClientRequest implements InvocationAware { + private final Invocation invocation; HttpClientRequestWrapper(Invocation invocation) { this.invocation = invocation; } - HttpClientRequestWrapper invocation(Invocation invocation) { - this.invocation = invocation; - return this; - } - - @Override public void header(String name, String value) { invocation.addContext(name, value); @@ -48,23 +38,33 @@ public String method() { return invocation.getOperationMeta().getHttpMethod(); } + @Override + public String route() { + return invocation.getEndpoint().getEndpoint(); + } + @Override public String path() { - return invocation.getOperationMeta().getOperationPath(); + return TracingConfiguration.createRequestPath(invocation); } @Override public String url() { - return invocation.getEndpoint().getEndpoint(); + return invocation.getInvocationQualifiedName(); } @Override public String header(String name) { - return invocation.getContext().get(name); + return invocation.getContext(name); } @Override public Object unwrap() { return invocation; } + + @Override + public Invocation getInvocation() { + return invocation; + } } diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientResponseWrapper.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientResponseWrapper.java index 3cc070505aa..b939efaa610 100644 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientResponseWrapper.java +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientResponseWrapper.java @@ -17,43 +17,23 @@ package org.apache.servicecomb.tracing.zipkin; -import brave.http.HttpClientRequest; -import brave.http.HttpClientResponse; -import brave.internal.Nullable; - +import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; -class HttpClientResponseWrapper extends HttpClientResponse { - @Nullable - HttpClientRequest request; - - @Nullable - Response response; +import brave.http.HttpClientRequest; +import brave.http.HttpClientResponse; - @Nullable - Throwable error; +class HttpClientResponseWrapper extends HttpClientResponse implements InvocationAware { + final HttpClientRequest request; - HttpClientResponseWrapper() { - } + final Response response; - HttpClientResponseWrapper(@Nullable Response response, @Nullable Throwable error) { - this.response = response; - this.error = error; - } + final Invocation invocation; - HttpClientResponseWrapper response(Response response) { + HttpClientResponseWrapper(Invocation invocation, Response response, HttpClientRequestWrapper request) { this.response = response; - return this; - } - - HttpClientResponseWrapper throwable(Throwable error) { - this.error = error; - return this; - } - - HttpClientResponseWrapper request(HttpClientRequest request) { this.request = request; - return this; + this.invocation = invocation; } @Override @@ -73,6 +53,11 @@ public HttpClientRequest request() { @Override public Throwable error() { - return error; + return response.isFailed() ? response.getResult() : null; + } + + @Override + public Invocation getInvocation() { + return invocation; } } diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServeRequestWrapper.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServeRequestWrapper.java index 5503cc485b6..45725539b46 100644 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServeRequestWrapper.java +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServeRequestWrapper.java @@ -17,25 +17,18 @@ package org.apache.servicecomb.tracing.zipkin; -import brave.http.HttpServerRequest; - +import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.Invocation; -class HttpServeRequestWrapper extends HttpServerRequest { - private Invocation invocation; +import brave.http.HttpServerRequest; - HttpServeRequestWrapper() { - } +class HttpServeRequestWrapper extends HttpServerRequest implements InvocationAware { + private final Invocation invocation; HttpServeRequestWrapper(Invocation invocation) { this.invocation = invocation; } - HttpServeRequestWrapper invocation(Invocation invocation) { - this.invocation = invocation; - return this; - } - @Override public String method() { return invocation.getOperationMeta().getHttpMethod(); @@ -43,21 +36,35 @@ public String method() { @Override public String path() { - return invocation.getOperationMeta().getOperationPath(); + return TracingConfiguration.createRequestPath(invocation); } @Override public String url() { - return invocation.getEndpoint().getEndpoint(); + return invocation.getInvocationQualifiedName(); } @Override - public String header(String key) { - return invocation.getContext().get(key); + public String route() { + return invocation.getRequestEx() != null ? invocation.getRequestEx().getRemoteAddr() : null; + } + + @Override + public String header(String name) { + String result = invocation.getContext(name); + if (StringUtils.isEmpty(result) && invocation.getRequestEx() != null) { + result = invocation.getRequestEx().getHeader(name); + } + return result; } @Override public Object unwrap() { return invocation; } + + @Override + public Invocation getInvocation() { + return invocation; + } } diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServerResponseWrapper.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServerResponseWrapper.java index ea01bea418a..9f384bdfffd 100644 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServerResponseWrapper.java +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServerResponseWrapper.java @@ -17,43 +17,23 @@ package org.apache.servicecomb.tracing.zipkin; -import brave.http.HttpServerRequest; -import brave.http.HttpServerResponse; -import brave.internal.Nullable; - +import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; -class HttpServerResponseWrapper extends HttpServerResponse { - @Nullable - private Response response; - - @Nullable - private Throwable error; +import brave.http.HttpServerRequest; +import brave.http.HttpServerResponse; - @Nullable - private HttpServerRequest request; +class HttpServerResponseWrapper extends HttpServerResponse implements InvocationAware { + private final Response response; - HttpServerResponseWrapper() { - } + private final HttpServerRequest request; - HttpServerResponseWrapper(Response response, Throwable error) { - this.response = response; - this.error = error; - } + private final Invocation invocation; - HttpServerResponseWrapper response(Response response) { + HttpServerResponseWrapper(Invocation invocation, Response response, HttpServeRequestWrapper request) { this.response = response; - return this; - } - - HttpServerResponseWrapper request(HttpServerRequest request) { this.request = request; - return this; - } - - HttpServerResponseWrapper error(Throwable error) { - this.error = error; - return this; + this.invocation = invocation; } @Override @@ -73,6 +53,11 @@ public HttpServerRequest request() { @Override public Throwable error() { - return error; + return response.isFailed() ? response.getResult() : null; + } + + @Override + public Invocation getInvocation() { + return invocation; } } diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/InvocationAware.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/InvocationAware.java new file mode 100644 index 00000000000..ddb4e8365ae --- /dev/null +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/InvocationAware.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.servicecomb.tracing.zipkin; + +import org.apache.servicecomb.core.Invocation; + +public interface InvocationAware { + Invocation getInvocation(); +} diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/LogSpanHandler.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/LogSpanHandler.java new file mode 100644 index 00000000000..93499ec9209 --- /dev/null +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/LogSpanHandler.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.servicecomb.tracing.zipkin; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brave.handler.MutableSpan; +import brave.handler.SpanHandler; +import brave.propagation.TraceContext; + +public class LogSpanHandler extends SpanHandler { + private static final Logger LOGGER = LoggerFactory.getLogger("scb-trace"); + + public boolean end(TraceContext context, MutableSpan span, Cause cause) { + if (!LOGGER.isInfoEnabled()) { + return false; + } else { + LOGGER.info(span.toString()); + return true; + } + } + + public String toString() { + return "LogSpanHandler{name=scb-trace}"; + } +} diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/TracingConfiguration.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/TracingConfiguration.java index 2ab531b8083..79dbc2857b6 100644 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/TracingConfiguration.java +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/TracingConfiguration.java @@ -26,11 +26,19 @@ import java.text.MessageFormat; +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.DynamicProperties; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; import brave.Tracing; import brave.context.slf4j.MDCScopeDecorator; @@ -38,75 +46,114 @@ import brave.propagation.CurrentTraceContext; import brave.propagation.ThreadLocalCurrentTraceContext; import zipkin2.Span; -import zipkin2.codec.SpanBytesEncoder; import zipkin2.reporter.AsyncReporter; +import zipkin2.reporter.BytesMessageSender; import zipkin2.reporter.Reporter; import zipkin2.reporter.Sender; +import zipkin2.reporter.SpanBytesEncoder; import zipkin2.reporter.brave.AsyncZipkinSpanHandler; import zipkin2.reporter.okhttp3.OkHttpSender; @Configuration @ConditionalOnProperty(value = TracingConfiguration.TRACING_ENABLED, - havingValue = "true") + havingValue = "true", matchIfMissing = true) public class TracingConfiguration { - public static final String TRACING_PREFIX = "servicecomb.tracing.zipkin"; + public static final String TRACING_PREFIX = "servicecomb.tracing"; public static final String TRACING_ENABLED = TRACING_PREFIX + ".enabled"; - private String apiVersion = CONFIG_TRACING_COLLECTOR_API_V2; - - @Bean - Sender sender(DynamicProperties dynamicProperties) { - apiVersion = dynamicProperties.getStringProperty(CONFIG_TRACING_COLLECTOR_API_VERSION, - CONFIG_TRACING_COLLECTOR_API_V2).toLowerCase(); - // use default value if the user set value is invalid - if (apiVersion.compareTo(CONFIG_TRACING_COLLECTOR_API_V1) != 0) { - apiVersion = CONFIG_TRACING_COLLECTOR_API_V2; + public static final String TRACING_REPORTER_LOG_ENABLED = TRACING_PREFIX + ".reporter.log.enabled"; + + public static final String TRACING_REPORTER_ZIPKIN_ENABLED = TRACING_PREFIX + ".reporter.zipkin.enabled"; + + @Configuration + @ConditionalOnProperty(value = TracingConfiguration.TRACING_REPORTER_ZIPKIN_ENABLED, + havingValue = "true") + static class ZipkinReporterConfiguration { + @Bean + BytesMessageSender scbOkHttpSender(DynamicProperties dynamicProperties) { + String apiVersion = dynamicProperties.getStringProperty(CONFIG_TRACING_COLLECTOR_API_VERSION, + CONFIG_TRACING_COLLECTOR_API_V2).toLowerCase(); + // use default value if the user set value is invalid + if (apiVersion.compareTo(CONFIG_TRACING_COLLECTOR_API_V1) != 0) { + apiVersion = CONFIG_TRACING_COLLECTOR_API_V2; + } + + String path = MessageFormat.format(CONFIG_TRACING_COLLECTOR_PATH, apiVersion); + return OkHttpSender.create( + dynamicProperties.getStringProperty( + CONFIG_TRACING_COLLECTOR_ADDRESS, + DEFAULT_TRACING_COLLECTOR_ADDRESS) + .trim() + .replaceAll("/+$", "") + .concat(path)); } - String path = MessageFormat.format(CONFIG_TRACING_COLLECTOR_PATH, apiVersion); - return OkHttpSender.create( - dynamicProperties.getStringProperty( - CONFIG_TRACING_COLLECTOR_ADDRESS, - DEFAULT_TRACING_COLLECTOR_ADDRESS) - .trim() - .replaceAll("/+$", "") - .concat(path)); - } + @Bean + Reporter scbZipkinReporter(DynamicProperties dynamicProperties, BytesMessageSender sender) { + String apiVersion = dynamicProperties.getStringProperty(CONFIG_TRACING_COLLECTOR_API_VERSION, + CONFIG_TRACING_COLLECTOR_API_V2).toLowerCase(); + if (apiVersion.compareTo(CONFIG_TRACING_COLLECTOR_API_V1) == 0) { + return AsyncReporter.builder(sender).build(SpanBytesEncoder.JSON_V1); + } - @Bean - Reporter zipkinReporter(Sender sender) { - if (apiVersion.compareTo(CONFIG_TRACING_COLLECTOR_API_V1) == 0) { - return AsyncReporter.builder(sender).build(SpanBytesEncoder.JSON_V1); + return AsyncReporter.builder(sender).build(); } - - return AsyncReporter.builder(sender).build(); } - @Bean - Tracing tracing(Sender sender, DynamicProperties dynamicProperties, - CurrentTraceContext currentTraceContext) { - return Tracing.newBuilder() - .localServiceName(BootStrapProperties.readServiceName()) - .currentTraceContext(currentTraceContext) // puts trace IDs into logs - .addSpanHandler(AsyncZipkinSpanHandler.create(sender)) + CurrentTraceContext scbCurrentTraceContext() { + return ThreadLocalCurrentTraceContext.newBuilder() + .addScopeDecorator(MDCScopeDecorator.newBuilder().build()) .build(); } @Bean - CurrentTraceContext currentTraceContext() { - return ThreadLocalCurrentTraceContext.newBuilder().addScopeDecorator(MDCScopeDecorator.newBuilder().build()) - .build(); + Tracing scbTracing(@Autowired(required = false) Sender sender, + CurrentTraceContext currentTraceContext, Environment environment, DynamicProperties dynamicProperties) { + Tracing.Builder builder = Tracing.newBuilder() + .localServiceName(BootStrapProperties.readServiceName(environment)) + .currentTraceContext(currentTraceContext); // puts trace IDs into logs + if (dynamicProperties.getBooleanProperty(TRACING_REPORTER_LOG_ENABLED, true)) { + builder.addSpanHandler(new LogSpanHandler()); + } + if (dynamicProperties.getBooleanProperty(TRACING_REPORTER_ZIPKIN_ENABLED, false)) { + builder.addSpanHandler(AsyncZipkinSpanHandler.create(sender)); + } + return builder.build(); } @Bean - HttpTracing httpTracing(Tracing tracing) { - return HttpTracing.create(tracing); + HttpTracing scbHttpTracing(Tracing tracing) { + return HttpTracing.newBuilder(tracing) + .clientRequestParser(new CustomHttpRequestParser()) + .clientResponseParser(new CustomHttpResponseParser()) + .serverRequestParser(new CustomHttpRequestParser()) + .serverResponseParser(new CustomHttpResponseParser()).build(); } @Bean - ZipkinTracingFilter zipkinTracingFilter() { + ZipkinTracingFilter scbZipkinTracingFilter() { return new ZipkinTracingFilter(); } + + public static String createRequestPath(Invocation invocation) { + URIEndpointObject address = (URIEndpointObject) invocation.getEndpoint().getAddress(); + String urlPrefix = address.getFirst(DefinitionConst.URL_PREFIX); + RestOperationMeta swaggerRestOperation = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); + String path = (String) invocation.getHandlerContext().get(RestConst.REST_CLIENT_REQUEST_PATH); + if (path == null) { + try { + path = swaggerRestOperation.getPathBuilder().createRequestPath(invocation.getSwaggerArguments()); + } catch (Exception e) { + path = invocation.getOperationMeta().getOperationPath(); + } + } + + if (StringUtils.isEmpty(urlPrefix) || path.startsWith(urlPrefix)) { + return path; + } + + return urlPrefix + path; + } } diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinConsumerDelegate.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinConsumerDelegate.java deleted file mode 100644 index 149dc8c6e6a..00000000000 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinConsumerDelegate.java +++ /dev/null @@ -1,75 +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.servicecomb.tracing.zipkin; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.swagger.invocation.Response; - -import brave.Span; -import brave.Tracing; -import brave.http.HttpClientRequest; -import brave.http.HttpClientResponse; -import brave.http.HttpClientHandler; -import brave.http.HttpTracing; -import brave.propagation.Propagation.Setter; -import brave.propagation.TraceContext.Injector; - -class ZipkinConsumerDelegate implements ZipkinTracingDelegate { - - private final HttpClientHandler handler; - - private final HttpTracing httpTracing; - - private final Injector injector; - - private final HttpClientResponseWrapper responseWrapper; - - private final HttpClientRequestWrapper requestWrapper; - - ZipkinConsumerDelegate(HttpTracing httpTracing) { - this.httpTracing = httpTracing; - this.injector = httpTracing.tracing().propagation().injector(injector()); - this.handler = HttpClientHandler.create(httpTracing); - this.responseWrapper = new HttpClientResponseWrapper(); - this.requestWrapper = new HttpClientRequestWrapper(); - } - - @Override - public Span createSpan(Invocation invocation) { - return handler.handleSend(requestWrapper.invocation(invocation)); - } - - @Override - public void onResponse(Span span, Response response, Throwable error) { - handler.handleReceive(responseWrapper.response(response).throwable(error).request(requestWrapper), span); - } - - @Override - public String name() { - return "Zipkin consumer"; - } - - @Override - public Tracing tracer() { - return httpTracing.tracing(); - } - - private Setter injector() { - return (invocation, key, value) -> invocation.getContext().put(key, value); - } -} diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinProviderDelegate.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinProviderDelegate.java deleted file mode 100644 index 0820f0c8c3a..00000000000 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinProviderDelegate.java +++ /dev/null @@ -1,100 +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.servicecomb.tracing.zipkin; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.core.Const; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.swagger.invocation.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brave.Span; -import brave.Tracing; -import brave.http.HttpServerRequest; -import brave.http.HttpServerResponse; -import brave.http.HttpServerHandler; -import brave.http.HttpTracing; -import brave.propagation.Propagation.Getter; -import brave.propagation.TraceContext.Extractor; - -class ZipkinProviderDelegate implements ZipkinTracingDelegate { - private static final Logger LOG = LoggerFactory.getLogger(ZipkinProviderDelegate.class); - - private final HttpServerHandler handler; - - private final HttpTracing httpTracing; - - private final Extractor extractor; - - private final HttpServeRequestWrapper requestWrapper; - - private final HttpServerResponseWrapper responseWrapper; - - public static final String SPAN_ID_HEADER_NAME = "X-B3-SpanId"; - - public static final String TRACE_ID_HEADER_NAME = Const.TRACE_ID_NAME; - - private static final Getter INVOCATION_STRING_GETTER = (invocation, key) -> { - String extracted = invocation.getContext().get(key); - if (StringUtils.isEmpty(extracted) && SPAN_ID_HEADER_NAME.equals(key)) { - // use traceId as spanId to avoid brave's recreating traceId - extracted = invocation.getContext().get(TRACE_ID_HEADER_NAME); - LOG.debug("try to extract X-B3-SpanId, but the value is empty, replace with TraceId = [{}]", extracted); - } - return extracted; - }; - - @VisibleForTesting - static Getter getInvocationStringGetter() { - return INVOCATION_STRING_GETTER; - } - - ZipkinProviderDelegate(HttpTracing httpTracing) { - this.httpTracing = httpTracing; - this.extractor = httpTracing.tracing().propagation().extractor(extractor()); - this.handler = HttpServerHandler.create(httpTracing); - this.requestWrapper = new HttpServeRequestWrapper(); - this.responseWrapper = new HttpServerResponseWrapper(); - } - - @Override - public Tracing tracer() { - return httpTracing.tracing(); - } - - @Override - public Span createSpan(Invocation invocation) { - return handler.handleReceive(requestWrapper.invocation(invocation)); - } - - @Override - public void onResponse(Span span, Response response, Throwable error) { - handler.handleSend(responseWrapper.response(response).error(error).request(requestWrapper), span); - } - - @Override - public String name() { - return "Zipkin provider"; - } - - private Getter extractor() { - return INVOCATION_STRING_GETTER; - } -} diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingDelegate.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingDelegate.java deleted file mode 100644 index 3761a8d8454..00000000000 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingDelegate.java +++ /dev/null @@ -1,35 +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.servicecomb.tracing.zipkin; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.swagger.invocation.Response; - -import brave.Span; -import brave.Tracing; - -interface ZipkinTracingDelegate { - - Tracing tracer(); - - Span createSpan(Invocation invocation); - - void onResponse(Span span, Response response, Throwable error); - - String name(); -} diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingFilter.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingFilter.java index a9d09abca2c..f88532adf5e 100644 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingFilter.java +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingFilter.java @@ -17,59 +17,113 @@ package org.apache.servicecomb.tracing.zipkin; -import static org.apache.servicecomb.swagger.invocation.InvocationType.PRODUCER; - -import java.util.concurrent.CompletableFuture; - -import javax.annotation.Nonnull; - import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.exception.Exceptions; -import org.apache.servicecomb.core.filter.Filter; -import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.InvocationStartEvent; +import org.apache.servicecomb.foundation.common.event.EventManager; import org.springframework.beans.factory.annotation.Autowired; +import com.google.common.eventbus.Subscribe; + import brave.Span; -import brave.Tracer.SpanInScope; +import brave.http.HttpClientHandler; +import brave.http.HttpClientRequest; +import brave.http.HttpClientResponse; +import brave.http.HttpServerHandler; +import brave.http.HttpServerRequest; +import brave.http.HttpServerResponse; import brave.http.HttpTracing; -public class ZipkinTracingFilter implements Filter { - public static final String NAME = "zipkin"; +public class ZipkinTracingFilter { + public static final String CONTEXT_TRACE_REQUEST = "x-trace-request"; + + public static final String CONTEXT_TRACE_HANDLER = "x-trace-handler"; - private ZipkinConsumerDelegate consumer; + public static final String CONTEXT_TRACE_SPAN = "x-trace-span"; - private ZipkinProviderDelegate producer; + public static final String CONTEXT_TRACE_EDGE_REQUEST = "x-trace-edge-request"; - @Nonnull - @Override - public String getName() { - return NAME; + public static final String CONTEXT_TRACE_EDGE_HANDLER = "x-trace-edge-handler"; + + public static final String CONTEXT_TRACE_EDGE_SPAN = "x-trace-edge-span"; + + private HttpTracing httpTracing; + + public ZipkinTracingFilter() { + EventManager.register(this); } @Autowired public void setHttpTracing(HttpTracing httpTracing) { - this.consumer = new ZipkinConsumerDelegate(httpTracing); - this.producer = new ZipkinProviderDelegate(httpTracing); + this.httpTracing = httpTracing; } - @SuppressWarnings({"try", "unused"}) - @Override - public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - ZipkinTracingDelegate tracing = collectTracing(invocation); + @Subscribe + public void onInvocationStartEvent(InvocationStartEvent event) { + Invocation invocation = event.getInvocation(); - Span span = tracing.createSpan(invocation); - try (SpanInScope scope = tracing.tracer().tracer().withSpanInScope(span)) { - return nextNode.onFilter(invocation) - .whenComplete((response, exception) -> tracing.onResponse(span, response, Exceptions.unwrap(exception))); + if (invocation.isProducer()) { + HttpServerHandler handler = HttpServerHandler.create(httpTracing); + HttpServeRequestWrapper request = new HttpServeRequestWrapper(invocation); + Span span = handler.handleReceive(request); + invocation.addLocalContext(CONTEXT_TRACE_SPAN, span); + invocation.addLocalContext(CONTEXT_TRACE_HANDLER, handler); + invocation.addLocalContext(CONTEXT_TRACE_REQUEST, request); + } else if (invocation.isConsumer()) { + Span parentSpan = invocation.getLocalContext(CONTEXT_TRACE_SPAN); + HttpClientHandler handler = HttpClientHandler.create(httpTracing); + HttpClientRequestWrapper request = new HttpClientRequestWrapper(invocation); + Span span = handler.handleSendWithParent(request, parentSpan == null ? null : parentSpan.context()); + invocation.addLocalContext(CONTEXT_TRACE_HANDLER, handler); + invocation.addLocalContext(CONTEXT_TRACE_REQUEST, request); + invocation.addLocalContext(CONTEXT_TRACE_SPAN, span); + } else { + // edge as server + HttpServerHandler serverHandler = HttpServerHandler.create(httpTracing); + HttpServeRequestWrapper serverRequest = new HttpServeRequestWrapper(invocation); + Span serverSpan = serverHandler.handleReceive(serverRequest); + invocation.addLocalContext(CONTEXT_TRACE_EDGE_SPAN, serverSpan); + invocation.addLocalContext(CONTEXT_TRACE_EDGE_HANDLER, serverHandler); + invocation.addLocalContext(CONTEXT_TRACE_EDGE_REQUEST, serverRequest); + + // edge as client + HttpClientHandler clientHandler = HttpClientHandler.create(httpTracing); + HttpClientRequestWrapper clientRequest = new HttpClientRequestWrapper(invocation); + Span clientSpan = clientHandler.handleSendWithParent(clientRequest, serverSpan.context()); + invocation.addLocalContext(CONTEXT_TRACE_HANDLER, clientHandler); + invocation.addLocalContext(CONTEXT_TRACE_REQUEST, clientRequest); + invocation.addLocalContext(CONTEXT_TRACE_SPAN, clientSpan); } } - private ZipkinTracingDelegate collectTracing(Invocation invocation) { - if (PRODUCER.equals(invocation.getInvocationType())) { - return producer; + @Subscribe + public void onInvocationFinishEvent(InvocationFinishEvent event) { + Invocation invocation = event.getInvocation(); + if (invocation.isProducer()) { + HttpServerHandler handler + = invocation.getLocalContext(CONTEXT_TRACE_HANDLER); + Span span = invocation.getLocalContext(CONTEXT_TRACE_SPAN); + handler.handleSend(new HttpServerResponseWrapper(invocation, event.getResponse(), + invocation.getLocalContext(CONTEXT_TRACE_REQUEST)), span); + } else if (invocation.isConsumer()) { + HttpClientHandler handler + = invocation.getLocalContext(CONTEXT_TRACE_HANDLER); + Span span = invocation.getLocalContext(CONTEXT_TRACE_SPAN); + handler.handleReceive(new HttpClientResponseWrapper(invocation, event.getResponse(), + invocation.getLocalContext(CONTEXT_TRACE_REQUEST)), span); + } else { + // edge as client + HttpClientHandler clientHandler + = invocation.getLocalContext(CONTEXT_TRACE_HANDLER); + Span clientSpan = invocation.getLocalContext(CONTEXT_TRACE_SPAN); + clientHandler.handleReceive(new HttpClientResponseWrapper(invocation, event.getResponse(), + invocation.getLocalContext(CONTEXT_TRACE_REQUEST)), clientSpan); + // edge as server + HttpServerHandler serverHandler + = invocation.getLocalContext(CONTEXT_TRACE_EDGE_HANDLER); + Span serverSpan = invocation.getLocalContext(CONTEXT_TRACE_EDGE_SPAN); + serverHandler.handleSend(new HttpServerResponseWrapper(invocation, event.getResponse(), + invocation.getLocalContext(CONTEXT_TRACE_EDGE_REQUEST)), serverSpan); } - - return consumer; } } diff --git a/handlers/handler-tracing-zipkin/src/main/resources/META-INF/spring.factories b/handlers/handler-tracing-zipkin/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 9333a2e6670..00000000000 --- a/handlers/handler-tracing-zipkin/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,19 +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. -## --------------------------------------------------------------------------- - -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.tracing.zipkin.TracingConfiguration diff --git a/handlers/handler-tracing-zipkin/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/handlers/handler-tracing-zipkin/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..f7edf1bb134 --- /dev/null +++ b/handlers/handler-tracing-zipkin/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.tracing.zipkin.TracingConfiguration diff --git a/handlers/handler-tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinProviderDelegateTest.java b/handlers/handler-tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinProviderDelegateTest.java deleted file mode 100644 index 68fd31bbe90..00000000000 --- a/handlers/handler-tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinProviderDelegateTest.java +++ /dev/null @@ -1,75 +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.servicecomb.tracing.zipkin; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.servicecomb.core.Invocation; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import brave.propagation.Propagation.Getter; - -public class ZipkinProviderDelegateTest { - @Test - public void testGetterOnGetSpanId() { - Getter getter = ZipkinProviderDelegate.getInvocationStringGetter(); - - Invocation invocation = Mockito.mock(Invocation.class); - Map context = new HashMap<>(); - - Mockito.when(invocation.getContext()).thenReturn(context); - - // if there is no spanId or traceId, then result is null - String spanId = getter.get(invocation, ZipkinProviderDelegate.SPAN_ID_HEADER_NAME); - Assertions.assertNull(spanId); - - // if there is no spanId but traceId, then traceId will be returned as result - final String testTraceId = "testTraceId"; - context.put(ZipkinProviderDelegate.TRACE_ID_HEADER_NAME, testTraceId); - spanId = getter.get(invocation, ZipkinProviderDelegate.SPAN_ID_HEADER_NAME); - Assertions.assertEquals(testTraceId, spanId); - - // if there is spanId, then spanId will be returned - final String testSpanId = "testSpanId"; - context.put(ZipkinProviderDelegate.SPAN_ID_HEADER_NAME, testSpanId); - spanId = getter.get(invocation, ZipkinProviderDelegate.SPAN_ID_HEADER_NAME); - Assertions.assertEquals(testSpanId, spanId); - } - - @Test - public void testGetterOnGetOtherContent() { - Getter getter = ZipkinProviderDelegate.getInvocationStringGetter(); - - Invocation invocation = Mockito.mock(Invocation.class); - Map context = new HashMap<>(); - - Mockito.when(invocation.getContext()).thenReturn(context); - - final String key = "key"; - String value = getter.get(invocation, key); - Assertions.assertNull(value); - - final String expectedValue = "value"; - context.put(key, expectedValue); - value = getter.get(invocation, key); - Assertions.assertEquals(expectedValue, value); - } -} diff --git a/handlers/pom.xml b/handlers/pom.xml index 800c25d6c1d..e74e2c95134 100644 --- a/handlers/pom.xml +++ b/handlers/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default 4.0.0 diff --git a/huawei-cloud/darklaunch/pom.xml b/huawei-cloud/darklaunch/pom.xml index 2675fd5eb26..b4396f87a64 100644 --- a/huawei-cloud/darklaunch/pom.xml +++ b/huawei-cloud/darklaunch/pom.xml @@ -21,7 +21,7 @@ huawei-cloud org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRule.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRule.java index 6dbb621b65e..988cd9e401f 100644 --- a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRule.java +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRule.java @@ -89,4 +89,3 @@ public void addRuleItem(DarklaunchRuleItem ruleItem) { this.ruleItems.add(ruleItem); } } - diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchServerListFilter.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchServerListFilter.java index 43772ac62d3..d380a8f254c 100644 --- a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchServerListFilter.java +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchServerListFilter.java @@ -22,12 +22,9 @@ import java.util.Random; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.loadbalance.ServerListFilterExt; import org.apache.servicecomb.loadbalance.ServiceCombServer; -import org.apache.servicecomb.registry.api.registry.Microservice; - -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicStringProperty; public class DarklaunchServerListFilter implements ServerListFilterExt { @@ -52,10 +49,8 @@ public boolean enabled() { @Override public List getFilteredListOfServers(List serverList, Invocation invocation) { - DynamicStringProperty ruleStr = DynamicPropertyFactory.getInstance().getStringProperty( - String.format(POLICY_SERVICE_COMB, invocation.getMicroserviceName()), null - ); - DarklaunchRule rule = DarklaunchRule.parse(ruleStr.get()); + DarklaunchRule rule = DarklaunchRule.parse(LegacyPropertyFactory + .getStringProperty(String.format(POLICY_SERVICE_COMB, invocation.getMicroserviceName()))); if (rule == null) { return serverList; } @@ -105,8 +100,8 @@ private void divideServerGroup(List serverList, DarklaunchRul for (ServiceCombServer server : serverList) { boolean hasGroup = false; for (DarklaunchRuleItem item : rule.getRuleItems()) { - Microservice microservice = MicroserviceCache.getInstance().getService(server.getInstance().getServiceId()); - item.getGroupCondition().setActual(DarklaunchRule.PROP_VERSION, microservice.getVersion()); + item.getGroupCondition() + .setActual(DarklaunchRule.PROP_VERSION, server.getInstance().getVersion()); if (item.getGroupCondition().match()) { item.addServer(server); hasGroup = true; diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/MicroserviceCache.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/MicroserviceCache.java deleted file mode 100644 index 42621d44d5f..00000000000 --- a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/MicroserviceCache.java +++ /dev/null @@ -1,41 +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.servicecomb.darklaunch; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.serviceregistry.RegistryUtils; - -public final class MicroserviceCache { - private static final MicroserviceCache instance = new MicroserviceCache(); - - private final Map services = new HashMap<>(); - - private MicroserviceCache() { - } - - public static MicroserviceCache getInstance() { - return instance; - } - - public Microservice getService(String serviceId) { - return services.computeIfAbsent(serviceId, (k) -> RegistryUtils.getMicroservice(serviceId)); - } -} diff --git a/huawei-cloud/darklaunch/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt b/huawei-cloud/darklaunch/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt index 6b7ea02f089..1104043c1cc 100644 --- a/huawei-cloud/darklaunch/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt +++ b/huawei-cloud/darklaunch/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.darklaunch.DarklaunchServerListFilter \ No newline at end of file +org.apache.servicecomb.darklaunch.DarklaunchServerListFilter diff --git a/huawei-cloud/dashboard/pom.xml b/huawei-cloud/dashboard/pom.xml index 165ce004246..015a43a240a 100644 --- a/huawei-cloud/dashboard/pom.xml +++ b/huawei-cloud/dashboard/pom.xml @@ -21,7 +21,7 @@ huawei-cloud org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -31,10 +31,6 @@ org.apache.servicecomb foundation-common - - org.apache.servicecomb - deployment - org.apache.servicecomb java-chassis-core diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DashboardConfiguration.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DashboardConfiguration.java new file mode 100644 index 00000000000..328b6bf6e02 --- /dev/null +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DashboardConfiguration.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.huaweicloud.dashboard.monitor; + +import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +@Configuration +public class DashboardConfiguration { + @Bean + public MonitorInformationCollector scbMonitorInformationCollector() { + return new MonitorInformationCollector(); + } + + @Bean + public MonitorConstant scbMonitorConstant(Environment environment) { + return new MonitorConstant(environment); + } + + @Bean + public MonitorBootListener scbMonitorBootListener() { + return new MonitorBootListener(); + } + + @Bean + public DataFactory scbDataFactory() { + return new DataFactory(); + } + + @Bean + public MetricsMonitorDataProvider scbMetricsMonitorDataProvider() { + return new MetricsMonitorDataProvider(); + } + + @Bean + public DefaultMonitorDataPublisher scbDefaultMonitorDataPublisher() { + return new DefaultMonitorDataPublisher(); + } +} diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DataFactory.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DataFactory.java index 12ba9052ab3..4889683c131 100644 --- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DataFactory.java +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DataFactory.java @@ -23,13 +23,12 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; -import javax.inject.Inject; - import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; import org.apache.servicecomb.huaweicloud.dashboard.monitor.model.MonitorDataProvider; import org.apache.servicecomb.huaweicloud.dashboard.monitor.model.MonitorDataPublisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import io.netty.util.concurrent.DefaultThreadFactory; @@ -40,12 +39,15 @@ public class DataFactory { private boolean hasStart = false; - @Inject + @Autowired private List dataProviders; - @Inject + @Autowired private MonitorDataPublisher publisher; + @Autowired + private MonitorConstant monitorConstant; + private ScheduledExecutorService executorService = null; @@ -81,7 +83,7 @@ void start() { } catch (Throwable e) { LOGGER.error("send monitor data error.", e); } - }, MonitorConstant.getInterval(), MonitorConstant.getInterval(), TimeUnit.MILLISECONDS); + }, monitorConstant.getInterval(), monitorConstant.getInterval(), TimeUnit.MILLISECONDS); hasStart = true; } } diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.java index de350f78779..1209d9d8532 100644 --- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.java +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.java @@ -17,12 +17,10 @@ package org.apache.servicecomb.huaweicloud.dashboard.monitor; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.commons.configuration.Configuration; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; @@ -33,8 +31,6 @@ import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.dashboard.client.DashboardAddressManager; import org.apache.servicecomb.dashboard.client.DashboardClient; -import org.apache.servicecomb.deployment.Deployment; -import org.apache.servicecomb.deployment.SystemBootstrapInfo; import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; @@ -44,12 +40,28 @@ import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; import org.apache.servicecomb.huaweicloud.dashboard.monitor.model.MonitorDataProvider; import org.apache.servicecomb.huaweicloud.dashboard.monitor.model.MonitorDataPublisher; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; public class DefaultMonitorDataPublisher implements MonitorDataPublisher { private static final String SSL_KEY = "mc.consumer"; private DashboardClient dashboardClient; + private MonitorConstant monitorConstant; + + private Environment environment; + + @Autowired + public void setMonitorConstant(MonitorConstant monitorConstant) { + this.monitorConstant = monitorConstant; + } + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + @Override public void init() { DashboardAddressManager addressManager = createDashboardAddressManager(); @@ -59,60 +71,60 @@ public void init() { requestBuilder.setSocketTimeout(10000); HttpTransport httpTransport = createHttpTransport(addressManager, requestBuilder.build(), - ConfigUtil.createLocalConfig()); + environment); dashboardClient = new DashboardClient(addressManager, httpTransport); } + @SuppressWarnings("unchecked") private DashboardAddressManager createDashboardAddressManager() { - List addresses = new ArrayList<>(); - SystemBootstrapInfo info = Deployment.getSystemBootStrapInfo( - MonitorConstant.SYSTEM_KEY_DASHBOARD_SERVICE); - if (info != null && info.getAccessURL() != null) { - addresses.addAll(info.getAccessURL()); - } + List addresses = ConfigUtil.parseArrayValue( + environment.getProperty(MonitorConstant.SYSTEM_KEY_DASHBOARD_SERVICE, "")); if (addresses.isEmpty()) { throw new IllegalStateException("dashboard address is not configured."); } + String region = environment.getProperty("servicecomb.datacenter.region"); + String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); - return new DashboardAddressManager(addresses, EventManager.getEventBus()); + return new DashboardAddressManager(addresses, EventManager.getEventBus(), region, availableZone); } private HttpTransport createHttpTransport(DashboardAddressManager addressManager, RequestConfig requestConfig, - Configuration localConfiguration) { + Environment environment) { List authHeaderProviders = SPIServiceUtils.getOrLoadSortedService(AuthHeaderProvider.class); - if (MonitorConstant.isProxyEnable()) { + if (monitorConstant.isProxyEnable()) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). setDefaultRequestConfig(requestConfig); - HttpHost proxy = new HttpHost(MonitorConstant.getProxyHost(), - MonitorConstant.getProxyPort(), "http"); // now only support http proxy + HttpHost proxy = new HttpHost(monitorConstant.getProxyHost(), + monitorConstant.getProxyPort(), "http"); // now only support http proxy httpClientBuilder.setProxy(proxy); CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(new AuthScope(proxy), - new UsernamePasswordCredentials(MonitorConstant.getProxyUsername(), - MonitorConstant.getProxyPasswd())); + new UsernamePasswordCredentials(monitorConstant.getProxyUsername(), + monitorConstant.getProxyPasswd())); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); return HttpTransportFactory .createHttpTransport( TransportUtils - .createSSLProperties(addressManager.sslEnabled(), localConfiguration, SSL_KEY), + .createSSLProperties(addressManager.sslEnabled(), environment, SSL_KEY), getRequestAuthHeaderProvider(authHeaderProviders), httpClientBuilder); } return HttpTransportFactory .createHttpTransport( TransportUtils - .createSSLProperties(MonitorConstant.sslEnabled(), localConfiguration, SSL_KEY), + .createSSLProperties(monitorConstant.sslEnabled(), environment, SSL_KEY), getRequestAuthHeaderProvider(authHeaderProviders), requestConfig); } private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List authHeaderProviders) { return signRequest -> { + String host = signRequest != null && signRequest.getEndpoint() != null ? signRequest.getEndpoint().getHost() : ""; Map headers = new HashMap<>(); - authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders())); + authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders(host))); return headers; }; } diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java index ea8f2de3883..ed54eef341e 100644 --- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java @@ -20,22 +20,26 @@ import java.util.List; import java.util.Map; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.dashboard.client.model.InterfaceInfo; import org.apache.servicecomb.dashboard.client.model.MonitorData; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.metrics.PolledEvent; +import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; import org.apache.servicecomb.huaweicloud.dashboard.monitor.model.MonitorDataProvider; -import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; import org.apache.servicecomb.metrics.core.publish.PublishModelFactory; import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroup; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; import org.apache.servicecomb.metrics.core.publish.model.invocation.PerfInfo; +import org.apache.servicecomb.registry.sc.SCRegistration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import com.google.common.eventbus.Subscribe; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.spectator.api.Meter; + +import io.micrometer.core.instrument.Meter; /** * Monitor data based on metrics-core module. @@ -52,15 +56,50 @@ public class MetricsMonitorDataProvider implements MonitorDataProvider { private volatile List meters = null; + private SCRegistration scRegistration; + + private Environment environment; + + private MonitorConstant monitorConstant; + public MetricsMonitorDataProvider() { EventManager.register(this); } + @Autowired + public void setSCRegistration(SCRegistration scRegistration) { + this.scRegistration = scRegistration; + } + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + public void setMonitorConstant(MonitorConstant monitorConstant) { + this.monitorConstant = monitorConstant; + } + @Override public boolean enabled() { - return DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.monitor.provider.metrics.enabled", true) - .get(); + return environment.getProperty("servicecomb.monitor.provider.metrics.enabled", boolean.class, true); + } + + @Override + public String getURL() { + return String.format(monitorConstant.getMonitorUri(), scRegistration.getMicroserviceInstance().getServiceName()); + } + + @Override + public void extractServiceInfo(MonitorData monitorData) { + monitorData.setAppId(scRegistration.getMicroserviceInstance().getApplication()); + monitorData.setName(scRegistration.getMicroserviceInstance().getServiceName()); + monitorData.setVersion(scRegistration.getMicroserviceInstance().getVersion()); + monitorData.setServiceId(scRegistration.getMicroserviceInstance().getBackendMicroservice().getServiceId()); + monitorData.setInstance(scRegistration.getMicroserviceInstance().getBackendMicroserviceInstance().getHostName()); + monitorData.setInstanceId(scRegistration.getMicroserviceInstance().getInstanceId()); + monitorData.setEnvironment(scRegistration.getMicroserviceInstance().getEnvironment()); } @Override @@ -91,7 +130,7 @@ private void extractProviderInfo(DefaultPublishModel model, Map { @@ -101,16 +140,16 @@ private void extractProviderInfo(DefaultPublishModel model, Map { @@ -137,16 +176,17 @@ private void extractEdgeInfo(DefaultPublishModel model, Map { @@ -173,16 +213,17 @@ private void extractConsumerInfo(DefaultPublishModel model, Map msAddresses = ConfigUtil.parseArrayValue(configuration.getString(MonitorConstant.MONITOR_URI)); - if (msAddresses.isEmpty()) { - return null; - } - SystemBootstrapInfo ms = new SystemBootstrapInfo(); - ms.setAccessURL(msAddresses); - return ms; - } - - @VisibleForTesting - public static void setConfiguration(AbstractConfiguration configuration) { - MonitorDefaultDeploymentProvider.configuration = configuration; - } -} diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorInformationCollector.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorInformationCollector.java index 30f765d3034..ef8b03a1368 100644 --- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorInformationCollector.java +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorInformationCollector.java @@ -17,25 +17,29 @@ package org.apache.servicecomb.huaweicloud.dashboard.monitor; +import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.core.bootup.BootUpInformationCollector; -import org.apache.servicecomb.deployment.Deployment; -import org.apache.servicecomb.deployment.SystemBootstrapInfo; import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; - -import java.util.Objects; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; public class MonitorInformationCollector implements BootUpInformationCollector { + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + @Override public String collect() { - return "monitor center: " + getCenterInfo(Deployment.getSystemBootStrapInfo(MonitorConstant.SYSTEM_KEY_DASHBOARD_SERVICE)); + return "monitor center: " + + ConfigUtil.parseArrayValue( + environment.getProperty(MonitorConstant.SYSTEM_KEY_DASHBOARD_SERVICE, "")); } @Override public int getOrder() { return 100; } - - private String getCenterInfo(SystemBootstrapInfo systemBootstrapInfo) { - return Objects.isNull(systemBootstrapInfo) ? "not exist" : systemBootstrapInfo.getAccessURL().toString(); - } } diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/TransportUtils.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/TransportUtils.java index 0f49400fecd..d58482d23af 100644 --- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/TransportUtils.java +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/TransportUtils.java @@ -19,13 +19,13 @@ import static org.apache.servicecomb.foundation.ssl.SSLOption.DEFAULT_OPTION; -import org.apache.commons.configuration.Configuration; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; +import org.springframework.core.env.Environment; public class TransportUtils { - public static SSLProperties createSSLProperties(boolean sslEnabled, Configuration configuration, String tag) { + public static SSLProperties createSSLProperties(boolean sslEnabled, Environment environment, String tag) { SSLProperties sslProperties = new SSLProperties(); sslProperties.setEnabled(sslEnabled); @@ -34,93 +34,93 @@ public static SSLProperties createSSLProperties(boolean sslEnabled, Configuratio } SSLOption option = new SSLOption(); - option.setEngine(getStringProperty(configuration, + option.setEngine(getStringProperty(environment, DEFAULT_OPTION.getEngine(), "ssl." + tag + ".engine", "ssl.engine")); option.setProtocols( - getStringProperty(configuration, + getStringProperty(environment, DEFAULT_OPTION.getProtocols(), "ssl." + tag + ".protocols", "ssl.protocols")); option.setCiphers( - getStringProperty(configuration, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers")); + getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers")); option.setAuthPeer( - getBooleanProperty(configuration, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer")); + getBooleanProperty(environment, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer")); option.setCheckCNHost( - getBooleanProperty(configuration, + getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNHost(), "ssl." + tag + ".checkCN.host", "ssl.checkCN.host")); option.setCheckCNWhite( - getBooleanProperty(configuration, + getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNWhite(), "ssl." + tag + ".checkCN.white", "ssl.checkCN.white")); - option.setCheckCNWhiteFile(getStringProperty(configuration, + option.setCheckCNWhiteFile(getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".checkCN.white.file", "ssl.checkCN.white.file")); - option.setAllowRenegociate(getBooleanProperty(configuration, - DEFAULT_OPTION.isAllowRenegociate(), - "ssl." + tag + ".allowRenegociate", - "ssl.allowRenegociate")); + option.setAllowRenegotiate(getBooleanProperty(environment, + DEFAULT_OPTION.isAllowRenegotiate(), + "ssl." + tag + ".allowRenegotiate", + "ssl.allowRenegotiate")); option.setStorePath( - getStringProperty(configuration, + getStringProperty(environment, DEFAULT_OPTION.getStorePath(), "ssl." + tag + ".storePath", "ssl.storePath")); option.setClientAuth( - getStringProperty(configuration, + getStringProperty(environment, DEFAULT_OPTION.getClientAuth(), "ssl." + tag + ".clientAuth", "ssl.clientAuth")); option.setTrustStore( - getStringProperty(configuration, + getStringProperty(environment, DEFAULT_OPTION.getTrustStore(), "ssl." + tag + ".trustStore", "ssl.trustStore")); - option.setTrustStoreType(getStringProperty(configuration, + option.setTrustStoreType(getStringProperty(environment, DEFAULT_OPTION.getTrustStoreType(), "ssl." + tag + ".trustStoreType", "ssl.trustStoreType")); - option.setTrustStoreValue(getStringProperty(configuration, + option.setTrustStoreValue(getStringProperty(environment, DEFAULT_OPTION.getTrustStoreValue(), "ssl." + tag + ".trustStoreValue", "ssl.trustStoreValue")); option.setKeyStore( - getStringProperty(configuration, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore")); + getStringProperty(environment, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore")); option.setKeyStoreType( - getStringProperty(configuration, + getStringProperty(environment, DEFAULT_OPTION.getKeyStoreType(), "ssl." + tag + ".keyStoreType", "ssl.keyStoreType")); - option.setKeyStoreValue(getStringProperty(configuration, + option.setKeyStoreValue(getStringProperty(environment, DEFAULT_OPTION.getKeyStoreValue(), "ssl." + tag + ".keyStoreValue", "ssl.keyStoreValue")); - option.setCrl(getStringProperty(configuration, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl")); + option.setCrl(getStringProperty(environment, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl")); option.setSslCustomClass( - getStringProperty(configuration, null, "ssl." + tag + ".sslCustomClass", "ssl.sslCustomClass")); + getStringProperty(environment, null, "ssl." + tag + ".sslCustomClass", "ssl.sslCustomClass")); sslProperties.setSslOption(option); sslProperties.setSslCustom(SSLCustom.createSSLCustom(option.getSslCustomClass())); return sslProperties; } - private static String getStringProperty(Configuration configuration, String defaultValue, String... keys) { + private static String getStringProperty(Environment environment, String defaultValue, String... keys) { for (String key : keys) { - if (configuration.containsKey(key)) { - return configuration.getString(key); + if (environment.getProperty(key) != null) { + return environment.getProperty(key); } } return defaultValue; } - private static boolean getBooleanProperty(Configuration configuration, boolean defaultValue, String... keys) { + private static boolean getBooleanProperty(Environment environment, boolean defaultValue, String... keys) { for (String key : keys) { - if (configuration.containsKey(key)) { - return configuration.getBoolean(key); + if (environment.getProperty(key) != null) { + return environment.getProperty(key, boolean.class, false); } } return defaultValue; diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/data/MonitorConstant.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/data/MonitorConstant.java index 9bf37e298a1..19b20787843 100644 --- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/data/MonitorConstant.java +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/data/MonitorConstant.java @@ -17,22 +17,9 @@ package org.apache.servicecomb.huaweicloud.dashboard.monitor.data; -import com.netflix.config.DynamicBooleanProperty; -import com.netflix.config.DynamicIntProperty; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicStringProperty; +import org.springframework.core.env.Environment; public class MonitorConstant { - public static final String DOMAIN_NAME = getDomainName(); - - public static final String CURRENT_VERSION = getApiVersion(); - - public static final String VERSION_V1 = "v1"; - - public static final String PREFIX_V2 = String.format("/v2/%s/csemonitor", DOMAIN_NAME); - - public static final String MONITORS_URI; - public static final String SYSTEM_KEY_DASHBOARD_SERVICE = "DashboardService"; public static final String MONITOR_URI = "servicecomb.monitor.client.serverUri"; @@ -53,99 +40,89 @@ public class MonitorConstant { public static final int DEFAULT_INTERVAL = 10000; - public static final int MAX_RETRY_TIMES = 3; + private final String DOMAIN_NAME; - static { - if (VERSION_V1.equals(CURRENT_VERSION)) { - MONITORS_URI = "/csemonitor/v1/metric?service=%s"; - } else { - MONITORS_URI = PREFIX_V2 + "/metric?service=%s"; - } - } + private final String CURRENT_VERSION; + + private final String VERSION_V1; + + private final String PREFIX_V2; + + private final String monitorsUri; + + private Environment environment; - public static final String TRANSACTION_URI; + public MonitorConstant(Environment environment) { + this.environment = environment; + this.VERSION_V1 = "v1"; + this.DOMAIN_NAME = getDomainName(); + this.PREFIX_V2 = String.format("/v2/%s/csemonitor", DOMAIN_NAME); + this.CURRENT_VERSION = getApiVersion(); - static { if (VERSION_V1.equals(CURRENT_VERSION)) { - TRANSACTION_URI = "/csemonitor/v1/transaction"; + monitorsUri = "/csemonitor/v1/metric?service=%s"; } else { - TRANSACTION_URI = PREFIX_V2 + "/transaction"; + monitorsUri = PREFIX_V2 + "/metric?service=%s"; } } - public static String getTanentName() { - DynamicStringProperty property = DynamicPropertyFactory.getInstance(). - getStringProperty("servicecomb.config.client.tenantName", "default"); - return property.getValue(); + public String getMonitorUri() { + return this.monitorsUri; } - public static String getDomainName() { - DynamicStringProperty property = DynamicPropertyFactory.getInstance(). - getStringProperty("servicecomb.config.client.domainName", "default"); - return property.getValue(); + public String getDomainName() { + return environment.getProperty("servicecomb.config.client.domainName", "default"); } - public static String getApiVersion() { - DynamicStringProperty property = DynamicPropertyFactory.getInstance(). - getStringProperty("servicecomb.monitor.client.api.version", "v2"); - return property.getValue(); + public String getApiVersion() { + return environment.getProperty("servicecomb.monitor.client.api.version", "v2"); } - public static String getServerUrl() { - DynamicStringProperty property = DynamicPropertyFactory.getInstance(). - getStringProperty("servicecomb.monitor.client.serverUri", null); - return property.getValue(); + public String getServerUrl() { + return environment.getProperty("servicecomb.monitor.client.serverUri"); } - public static boolean sslEnabled() { - DynamicBooleanProperty property = DynamicPropertyFactory.getInstance(). - getBooleanProperty("servicecomb.monitor.client.sslEnabled", true); - return property.getValue(); + public boolean sslEnabled() { + return environment.getProperty("servicecomb.monitor.client.sslEnabled", boolean.class, true); } - public static boolean isMonitorEnabled() { - DynamicBooleanProperty property = DynamicPropertyFactory.getInstance(). - getBooleanProperty("servicecomb.monitor.client.enabled", false); - return property.getValue(); + public boolean isMonitorEnabled() { + return environment.getProperty("servicecomb.monitor.client.enabled", boolean.class, false); } - public static int getConnectionTimeout() { - DynamicIntProperty property = DynamicPropertyFactory.getInstance(). - getIntProperty("servicecomb.monitor.client.timeout", DEFAULT_TIMEOUT); - return property.getValue(); + public int getConnectionTimeout() { + return environment.getProperty("servicecomb.monitor.client.timeout", int.class, DEFAULT_TIMEOUT); } - public static int getInterval() { - DynamicIntProperty property = DynamicPropertyFactory.getInstance(). - getIntProperty("servicecomb.monitor.client.interval", DEFAULT_INTERVAL); - int val = property.getValue(); - return Math.max(val, MIN_INTERVAL_MILLISECONDS); + public int getInterval() { + return Math.max(environment.getProperty("servicecomb.monitor.client.interval", int.class, DEFAULT_INTERVAL), + MIN_INTERVAL_MILLISECONDS); } - public static Boolean isProxyEnable() { + public Boolean isProxyEnable() { return Boolean.parseBoolean(getProperty("false", PROXY_ENABLE)); } - public static String getProxyHost() { + public String getProxyHost() { return getProperty("127.0.0.1", PROXY_HOST); } - public static int getProxyPort() { + public int getProxyPort() { return Integer.parseInt(getProperty("8080", PROXY_PORT)); } - public static String getProxyUsername() { + public String getProxyUsername() { return getProperty(null, PROXY_USERNAME); } - public static String getProxyPasswd() { + public String getProxyPasswd() { return getProperty(null, PROXY_PASSWD); } - private static String getProperty(String defaultValue, String... keys) { + private String getProperty(String defaultValue, String... keys) { String property = null; for (String key : keys) { - property = DynamicPropertyFactory.getInstance().getStringProperty(key, null).get(); + property = environment.getProperty(key); if (property != null) { break; } diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/model/MonitorDataProvider.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/model/MonitorDataProvider.java index 1dc5787fca4..4f88a5a4492 100644 --- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/model/MonitorDataProvider.java +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/model/MonitorDataProvider.java @@ -28,29 +28,13 @@ import org.apache.servicecomb.dashboard.client.model.MonitorData; import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.CPUMonitorCalc; -import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; -import org.apache.servicecomb.serviceregistry.RegistryUtils; public interface MonitorDataProvider { boolean enabled(); - default String getURL() { - return String.format(MonitorConstant.MONITORS_URI, RegistryUtils.getMicroservice().getServiceName()); - } + String getURL(); - default void extractServiceInfo(MonitorData monitorData) { - Microservice microservice = RegistryUtils.getMicroservice(); - MicroserviceInstance microserviceInstance = RegistryUtils.getMicroserviceInstance(); - monitorData.setAppId(microservice.getAppId()); - monitorData.setName(microservice.getServiceName()); - monitorData.setVersion(microservice.getVersion()); - monitorData.setServiceId(microservice.getServiceId()); - monitorData.setInstance(microserviceInstance.getHostName()); - monitorData.setInstanceId(microserviceInstance.getInstanceId()); - monitorData.setEnvironment(microservice.getEnvironment()); - } + void extractServiceInfo(MonitorData monitorData); default void exactProcessInfo(MonitorData monitorData) { MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); diff --git a/huawei-cloud/dashboard/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector b/huawei-cloud/dashboard/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector index 39268d6eca3..008bc6a1526 100644 --- a/huawei-cloud/dashboard/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector +++ b/huawei-cloud/dashboard/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -org.apache.servicecomb.huaweicloud.dashboard.monitor.MonitorInformationCollector \ No newline at end of file +org.apache.servicecomb.huaweicloud.dashboard.monitor.MonitorInformationCollector diff --git a/huawei-cloud/dashboard/src/main/resources/META-INF/services/org.apache.servicecomb.deployment.DeploymentProvider b/huawei-cloud/dashboard/src/main/resources/META-INF/services/org.apache.servicecomb.deployment.DeploymentProvider deleted file mode 100644 index 38547076ba5..00000000000 --- a/huawei-cloud/dashboard/src/main/resources/META-INF/services/org.apache.servicecomb.deployment.DeploymentProvider +++ /dev/null @@ -1,17 +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. -# -org.apache.servicecomb.huaweicloud.dashboard.monitor.MonitorDefaultDeploymentProvider \ No newline at end of file diff --git a/huawei-cloud/dashboard/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/huawei-cloud/dashboard/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..54229959fcb --- /dev/null +++ b/huawei-cloud/dashboard/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.huaweicloud.dashboard.monitor.DashboardConfiguration diff --git a/huawei-cloud/dashboard/src/main/resources/META-INF/spring/services.bean.xml b/huawei-cloud/dashboard/src/main/resources/META-INF/spring/services.bean.xml deleted file mode 100644 index bd26a5acf52..00000000000 --- a/huawei-cloud/dashboard/src/main/resources/META-INF/spring/services.bean.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - diff --git a/huawei-cloud/dashboard/src/test/resources/microservice.yaml b/huawei-cloud/dashboard/src/test/resources/microservice.yaml index 7af503301ec..363e3369fa0 100644 --- a/huawei-cloud/dashboard/src/test/resources/microservice.yaml +++ b/huawei-cloud/dashboard/src/test/resources/microservice.yaml @@ -39,4 +39,4 @@ servicecomb: customLabel: public rest: - address: 0.0.0.0:9097 # should be same with server.port to use web container \ No newline at end of file + address: 0.0.0.0:9097 # should be same with server.port to use web container diff --git a/huawei-cloud/environment/README.md b/huawei-cloud/environment/README.md deleted file mode 100644 index a006657ade8..00000000000 --- a/huawei-cloud/environment/README.md +++ /dev/null @@ -1,20 +0,0 @@ -这个模块主要提供 servicestage 部署环境变量和配置项的映射,通过这种方式可以简化配置。 - -servicestage 存在环境、应用、组件等概念,这些内容都是在部署的时候输入,可以将这些内容用于微服务的配置信息。 - -|概念|环境变量|微服务配置项| -| :--- | :--- | :--- | -|应用|CAS_APPLICATION_NAME|servicecomb.service.application| -|组件|CAS_COMPONENT_NAME|servicecomb.service.name| -|组件版本|CAS_INSTANCE_VERSION|servicecomb.service.version| - -部署的过程还会注入环境里面的资源信息。 - -|概念|环境变量|微服务配置项| -| :--- | :--- | :--- | -|服务/配置中心地址(逻辑多租,APIG场景)| PAAS_CSE_ENDPOINT | servicecomb.service.registry.address
servicecomb.config.client.serverUri| -|服务中心地址 | PAAS_CSE_SC_ENDPOINT | servicecomb.service.registry.address | -|配置中心地址 | PAAS_CSE_CC_ENDPOINT | servicecomb.config.client.serverUri | -|项目(区域) | PAAS_PROJECT_NAME | servicecomb.credentials.project | - - diff --git a/huawei-cloud/environment/pom.xml b/huawei-cloud/environment/pom.xml deleted file mode 100644 index 57cba6689c1..00000000000 --- a/huawei-cloud/environment/pom.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - huawei-cloud - org.apache.servicecomb - 3.0.0-SNAPSHOT - - 4.0.0 - servicestage-environment - Java Chassis::Huawei Cloud::ServiceStage - - - - org.apache.servicecomb - java-chassis-core - - - org.apache.servicecomb - registry-service-center - - - - \ No newline at end of file diff --git a/huawei-cloud/environment/src/main/resources/mapping.yaml b/huawei-cloud/environment/src/main/resources/mapping.yaml deleted file mode 100644 index 44418439165..00000000000 --- a/huawei-cloud/environment/src/main/resources/mapping.yaml +++ /dev/null @@ -1,43 +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. -# -servicecomb-config-order: -100 - -PAAS_CSE_ENDPOINT: - - servicecomb.service.registry.address - - servicecomb.config.client.serverUri - - servicecomb.monitor.client.serverUri -PAAS_CSE_SC_ENDPOINT: - - servicecomb.service.registry.address -PAAS_CSE_CC_ENDPOINT: - - servicecomb.config.client.serverUri -PAAS_CSE_DS_ENDPOINT: - - servicecomb.monitor.client.serverUri -PAAS_PROJECT_NAME: - - servicecomb.credentials.project - -## other possible and useful mappings - -#CAS_APPLICATION_NAME: -# - servicecomb.service.application -#CAS_COMPONENT_NAME: -# - servicecomb.service.name -#CAS_INSTANCE_VERSION: -# - servicecomb.service.version -#SERVICECOMB_SERVICE_PROPS: -# - servicecomb.service.properties -#SERVICECOMB_INSTANCE_PROPS: -# - servicecomb.instance.properties diff --git a/huawei-cloud/pom.xml b/huawei-cloud/pom.xml index 148a37aeccf..eb70052ed01 100644 --- a/huawei-cloud/pom.xml +++ b/huawei-cloud/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default @@ -32,9 +32,8 @@ servicestage - environment dashboard darklaunch - \ No newline at end of file + diff --git a/huawei-cloud/servicestage/pom.xml b/huawei-cloud/servicestage/pom.xml index 5b47e81ba68..d92064951f9 100644 --- a/huawei-cloud/servicestage/pom.xml +++ b/huawei-cloud/servicestage/pom.xml @@ -21,7 +21,7 @@ huawei-cloud org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 servicestage @@ -47,4 +47,4 @@
- \ No newline at end of file + diff --git a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/AKSKAuthHeaderProvider.java b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/AKSKAuthHeaderProvider.java index f291000297c..e80e867cab4 100644 --- a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/AKSKAuthHeaderProvider.java +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/AKSKAuthHeaderProvider.java @@ -17,9 +17,9 @@ package org.apache.servicecomb.huaweicloud.servicestage; -import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -28,18 +28,18 @@ import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Hex; -import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; import org.apache.servicecomb.foundation.auth.Cipher; import org.apache.servicecomb.foundation.auth.DefaultCipher; import org.apache.servicecomb.foundation.auth.ShaAKSKCipher; +import org.apache.servicecomb.foundation.bootstrap.BootStrapService; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; -public class AKSKAuthHeaderProvider implements AuthHeaderProvider { +public class AKSKAuthHeaderProvider implements AuthHeaderProvider, BootStrapService { private static final Logger LOGGER = LoggerFactory.getLogger(AKSKAuthHeaderProvider.class); private static final String CONFIG_AKSK_ENABLED = "servicecomb.credentials.akskEnabled"; @@ -62,34 +62,25 @@ public class AKSKAuthHeaderProvider implements AuthHeaderProvider { private static final String X_SERVICE_PROJECT = "X-Service-Project"; - private final Map headers = new HashMap<>(); - - private final Configuration configuration; + private static Environment environment; - private boolean enabled; + private final Map headers = new HashMap<>(); - private final boolean loaded = false; + private boolean loaded = false; public AKSKAuthHeaderProvider() { - this(ConfigUtil.createLocalConfig()); - } - - public AKSKAuthHeaderProvider(Configuration configuration) { - this.configuration = configuration; - this.enabled = configuration.getBoolean(CONFIG_AKSK_ENABLED, true); } public Map authHeaders() { - if (!enabled) { - return headers; + if (!environment.getProperty(CONFIG_AKSK_ENABLED, boolean.class, true)) { + return Collections.emptyMap(); } if (StringUtils.isEmpty(getAccessKey())) { LOGGER.warn("ak sk auth enabled but access key is not configured, disable it at runtime. " + "Config [{}] to false to disable it implicitly.", CONFIG_AKSK_ENABLED); - enabled = false; - return headers; + return Collections.emptyMap(); } if (!loaded) { @@ -103,19 +94,20 @@ private synchronized void load() { headers.put(X_SERVICE_AK, getAccessKey()); headers.put(X_SERVICE_SHAAKSK, getSecretKey()); headers.put(X_SERVICE_PROJECT, getProject()); + loaded = true; } } private String getAccessKey() { - return configuration.getString(CONFIG_ACCESS_KEY, ""); + return environment.getProperty(CONFIG_ACCESS_KEY, ""); } private String getCipher() { - return configuration.getString(CONFIG_CIPHER, VALUE_DEFAULT_CIPHER); + return environment.getProperty(CONFIG_CIPHER, VALUE_DEFAULT_CIPHER); } private String getSecretKey() { - String secretKey = configuration.getString(CONFIG_SECRET_KEY, ""); + String secretKey = environment.getProperty(CONFIG_SECRET_KEY, ""); String decodedSecretKey = new String(findCipher().decrypt(secretKey.toCharArray())); // ShaAKSKCipher 不解密, 认证的时候不处理;其他算法解密为 plain,需要 encode 为 ShaAKSKCipher 去认证。 @@ -127,15 +119,11 @@ private String getSecretKey() { } private String getProject() { - String project = configuration.getString(CONFIG_PROJECT, VALUE_DEFAULT_PROJECT); + String project = environment.getProperty(CONFIG_PROJECT, VALUE_DEFAULT_PROJECT); if (StringUtils.isEmpty(project)) { return project; } - try { - return URLEncoder.encode(project, "UTF-8"); - } catch (UnsupportedEncodingException e) { - return project; - } + return URLEncoder.encode(project, StandardCharsets.UTF_8); } private Cipher findCipher() { @@ -159,4 +147,9 @@ public static String sha256Encode(String key, String data) { throw new IllegalArgumentException("Can not encode ak sk. Please check the value is correct.", e); } } + + @Override + public void startup(Environment environment) { + AKSKAuthHeaderProvider.environment = environment; + } } diff --git a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/CasEnvConfig.java b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/CasEnvConfig.java index 93c93fbcf50..baadb927b40 100644 --- a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/CasEnvConfig.java +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/CasEnvConfig.java @@ -25,8 +25,7 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; - -import com.netflix.config.DynamicPropertyFactory; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; public class CasEnvConfig { private static final String APPLICATION_ID = "CAS_APPLICATION_ID"; @@ -51,36 +50,36 @@ private Map parseProps(String value) { return rs; } return Arrays.stream(value.split(",")).map(v -> v.split(":")) - .filter(v -> v.length == 2) - .collect(Collectors.toMap(v -> v[0], v -> v[1])); + .filter(v -> v.length == 2) + .collect(Collectors.toMap(v -> v[0], v -> v[1])); } public Map getNonEmptyInstanceProperties() { Map map = new HashMap<>(); - map.put(APPLICATION_ID, DynamicPropertyFactory - .getInstance().getStringProperty(APPLICATION_ID, EMPTY).get()); - map.put(COMPONENT_NAME, DynamicPropertyFactory - .getInstance().getStringProperty(COMPONENT_NAME, EMPTY).get()); - map.put(INSTANCE_VERSION, DynamicPropertyFactory - .getInstance().getStringProperty(INSTANCE_VERSION, EMPTY).get()); - map.put(INSTANCE_ID, DynamicPropertyFactory - .getInstance().getStringProperty(INSTANCE_ID, EMPTY).get()); - map.put(ENVIRONMENT_ID, DynamicPropertyFactory - .getInstance().getStringProperty(ENVIRONMENT_ID, EMPTY).get()); + map.put(APPLICATION_ID, LegacyPropertyFactory + .getStringProperty(APPLICATION_ID, EMPTY)); + map.put(COMPONENT_NAME, LegacyPropertyFactory + .getStringProperty(COMPONENT_NAME, EMPTY)); + map.put(INSTANCE_VERSION, LegacyPropertyFactory + .getStringProperty(INSTANCE_VERSION, EMPTY)); + map.put(INSTANCE_ID, LegacyPropertyFactory + .getStringProperty(INSTANCE_ID, EMPTY)); + map.put(ENVIRONMENT_ID, LegacyPropertyFactory + .getStringProperty(ENVIRONMENT_ID, EMPTY)); Map instanceProps = map.entrySet().stream() - .filter(entry -> StringUtils.isNotEmpty(entry.getValue())) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + .filter(entry -> StringUtils.isNotEmpty(entry.getValue())) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - instanceProps.putAll(parseProps(DynamicPropertyFactory - .getInstance().getStringProperty(INSTANCE_PROPS, EMPTY).get())); + instanceProps.putAll(parseProps(LegacyPropertyFactory + .getStringProperty(INSTANCE_PROPS, EMPTY))); return instanceProps; } public Map getNonEmptyServiceProperties() { - return parseProps(DynamicPropertyFactory - .getInstance().getStringProperty(SERVICE_PROPS, EMPTY).get()); + return parseProps(LegacyPropertyFactory + .getStringProperty(SERVICE_PROPS, EMPTY)); } } diff --git a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/CasEnvVariablesAdapter.java b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/CasEnvVariablesAdapter.java index af5941d43bd..41cfc848df6 100644 --- a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/CasEnvVariablesAdapter.java +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/CasEnvVariablesAdapter.java @@ -17,30 +17,26 @@ package org.apache.servicecomb.huaweicloud.servicestage; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.serviceregistry.adapter.EnvAdapter; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; +import java.util.Map; -public class CasEnvVariablesAdapter implements EnvAdapter { - private static final String NAME = "cas-env-variables-adapter"; +import org.apache.servicecomb.registry.RegistrationManager; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; - @Override - public String getEnvName() { - return NAME; - } +public class CasEnvVariablesAdapter implements InitializingBean { - @Override - public int getOrder() { - return 0; - } + private RegistrationManager registrationManager; - @Override - public void beforeRegisterInstance(MicroserviceInstance instance) { - instance.getProperties().putAll(CasEnvConfig.INSTANCE.getNonEmptyInstanceProperties()); + @Autowired + public void setRegistrationManager(RegistrationManager registrationManager) { + this.registrationManager = registrationManager; } @Override - public void beforeRegisterService(Microservice microservice) { - microservice.getProperties().putAll(CasEnvConfig.INSTANCE.getNonEmptyServiceProperties()); + public void afterPropertiesSet() throws Exception { + Map extras = CasEnvConfig.INSTANCE.getNonEmptyServiceProperties(); + extras.forEach((k, v) -> this.registrationManager.addProperty(k, v)); + extras = CasEnvConfig.INSTANCE.getNonEmptyInstanceProperties(); + extras.forEach((k, v) -> this.registrationManager.addProperty(k, v)); } } diff --git a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/RBACBootStrapService.java b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/RBACBootStrapService.java new file mode 100644 index 00000000000..007e3e56c96 --- /dev/null +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/RBACBootStrapService.java @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.huaweicloud.servicestage; + +import static org.apache.servicecomb.foundation.ssl.SSLOption.DEFAULT_OPTION; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.servicecomb.foundation.auth.Cipher; +import org.apache.servicecomb.foundation.auth.DefaultCipher; +import org.apache.servicecomb.foundation.bootstrap.BootStrapService; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.foundation.ssl.SSLCustom; +import org.apache.servicecomb.foundation.ssl.SSLOption; +import org.apache.servicecomb.foundation.vertx.VertxConst; +import org.apache.servicecomb.http.client.auth.DefaultRequestAuthHeaderProvider; +import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; +import org.apache.servicecomb.http.client.common.HttpTransport; +import org.apache.servicecomb.http.client.common.HttpTransportFactory; +import org.apache.servicecomb.registry.sc.SCClientUtils; +import org.apache.servicecomb.service.center.client.ServiceCenterAddressManager; +import org.apache.servicecomb.service.center.client.ServiceCenterClient; +import org.apache.servicecomb.service.center.client.ServiceCenterRawClient; +import org.springframework.core.env.Environment; + +import com.google.common.annotations.VisibleForTesting; + +public class RBACBootStrapService implements BootStrapService { + private static final String RBAC_ADDRESS = "servicecomb.service.registry.address"; + + public static final String DEFAULT_REGISTRY_NAME = "default"; + + public static final String RBAC_ENABLED = "servicecomb.credentials.rbac.enabled"; + + public static final String ACCOUNT_NAME_KEY = "servicecomb.credentials.account.name"; + + public static final String PASSWORD_KEY = "servicecomb.credentials.account.password"; + + public static final String CIPHER_KEY = "servicecomb.credentials.cipher"; + + public static final String PROJECT_KEY = "servicecomb.credentials.project"; + + private static final String SSL_TAG = "sc.consumer"; + + @Override + public void startup(Environment environment) { + if (!getBooleanProperty(environment, false, RBAC_ENABLED)) { + return; + } + + ServiceCenterAddressManager addressManager = createAddressManager(environment); + addressManager.setEventBus(EventManager.getEventBus()); + SSLProperties sslProperties = createSSLProperties(environment); + sslProperties.setEnabled(addressManager.sslEnabled()); + + ServiceCenterClient serviceCenterClient = + new ServiceCenterClient(new ServiceCenterRawClient.Builder() + .setTenantName("default") + .setAddressManager(addressManager) + .setHttpTransport(createHttpTransport(environment, sslProperties)).build(), addressManager); + + Map clients = new HashMap<>(1); + clients.put(DEFAULT_REGISTRY_NAME, serviceCenterClient); + TokenCacheManager.getInstance().setServiceCenterClients(clients); + TokenCacheManager.getInstance().addTokenCache( + DEFAULT_REGISTRY_NAME, + getStringProperty(environment, null, ACCOUNT_NAME_KEY), + getStringProperty(environment, null, PASSWORD_KEY), + getCipher(getStringProperty(environment, DefaultCipher.CIPHER_NAME, CIPHER_KEY))); + } + + private static HttpTransport createHttpTransport(Environment environment, SSLProperties sslProperties) { + if (isProxyEnable(environment)) { + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). + setDefaultRequestConfig(HttpTransportFactory.defaultRequestConfig().build()); + HttpHost proxy = new HttpHost(getProxyHost(environment), + getProxyPort(environment), "http"); // now only support http proxy + httpClientBuilder.setProxy(proxy); + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(new AuthScope(proxy), + new UsernamePasswordCredentials(getProxyUsername(environment), + getProxyPasswd(environment))); + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + + return HttpTransportFactory + .createHttpTransport(sslProperties, + new DefaultRequestAuthHeaderProvider(), httpClientBuilder); + } + + return HttpTransportFactory + .createHttpTransport(sslProperties, + new DefaultRequestAuthHeaderProvider(), HttpTransportFactory.defaultRequestConfig().build()); + } + + @VisibleForTesting + Cipher getCipher(String cipherName) { + if (DefaultCipher.CIPHER_NAME.equals(cipherName)) { + return DefaultCipher.getInstance(); + } + + List ciphers = SPIServiceUtils.getOrLoadSortedService(Cipher.class); + return ciphers.stream().filter(c -> c.name().equals(cipherName)).findFirst() + .orElseThrow(() -> new IllegalArgumentException("failed to find cipher named " + cipherName)); + } + + private ServiceCenterAddressManager createAddressManager(Environment environment) { + return SCClientUtils.createAddressManager(getProjectName(environment), getRBACAddressList(environment), + environment); + } + + private SSLProperties createSSLProperties(Environment environment) { + SSLProperties sslProperties = new SSLProperties(); + + SSLOption option = new SSLOption(); + option.setEngine(getStringProperty(environment, + DEFAULT_OPTION.getEngine(), + "ssl." + SSL_TAG + ".engine", + "ssl.engine")); + option.setProtocols( + getStringProperty(environment, + DEFAULT_OPTION.getProtocols(), + "ssl." + SSL_TAG + ".protocols", + "ssl.protocols")); + option.setCiphers( + getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + SSL_TAG + ".ciphers", "ssl.ciphers")); + option.setAuthPeer( + getBooleanProperty(environment, DEFAULT_OPTION.isAuthPeer(), "ssl." + SSL_TAG + ".authPeer", "ssl.authPeer")); + option.setCheckCNHost( + getBooleanProperty(environment, + DEFAULT_OPTION.isCheckCNHost(), + "ssl." + SSL_TAG + ".checkCN.host", + "ssl.checkCN.host")); + option.setCheckCNWhite( + getBooleanProperty(environment, + DEFAULT_OPTION.isCheckCNWhite(), + "ssl." + SSL_TAG + ".checkCN.white", + "ssl.checkCN.white")); + option.setCheckCNWhiteFile(getStringProperty(environment, + DEFAULT_OPTION.getCiphers(), + "ssl." + SSL_TAG + ".checkCN.white.file", + "ssl.checkCN.white.file")); + option.setAllowRenegotiate(getBooleanProperty(environment, + DEFAULT_OPTION.isAllowRenegotiate(), + "ssl." + SSL_TAG + ".allowRenegotiate", + "ssl.allowRenegotiate")); + option.setStorePath( + getStringProperty(environment, + DEFAULT_OPTION.getStorePath(), + "ssl." + SSL_TAG + ".storePath", + "ssl.storePath")); + option.setClientAuth( + getStringProperty(environment, + DEFAULT_OPTION.getClientAuth(), + "ssl." + SSL_TAG + ".clientAuth", + "ssl.clientAuth")); + option.setTrustStore( + getStringProperty(environment, + DEFAULT_OPTION.getTrustStore(), + "ssl." + SSL_TAG + ".trustStore", + "ssl.trustStore")); + option.setTrustStoreType(getStringProperty(environment, + DEFAULT_OPTION.getTrustStoreType(), + "ssl." + SSL_TAG + ".trustStoreType", + "ssl.trustStoreType")); + option.setTrustStoreValue(getStringProperty(environment, + DEFAULT_OPTION.getTrustStoreValue(), + "ssl." + SSL_TAG + ".trustStoreValue", + "ssl.trustStoreValue")); + option.setKeyStore( + getStringProperty(environment, DEFAULT_OPTION.getKeyStore(), "ssl." + SSL_TAG + ".keyStore", "ssl.keyStore")); + option.setKeyStoreType( + getStringProperty(environment, + DEFAULT_OPTION.getKeyStoreType(), + "ssl." + SSL_TAG + ".keyStoreType", + "ssl.keyStoreType")); + option.setKeyStoreValue(getStringProperty(environment, + DEFAULT_OPTION.getKeyStoreValue(), + "ssl." + SSL_TAG + ".keyStoreValue", + "ssl.keyStoreValue")); + option.setCrl(getStringProperty(environment, DEFAULT_OPTION.getCrl(), "ssl." + SSL_TAG + ".crl", "ssl.crl")); + option.setSslCustomClass( + getStringProperty(environment, null, "ssl." + SSL_TAG + ".sslCustomClass", "ssl.sslCustomClass")); + + sslProperties.setSslOption(option); + sslProperties.setSslCustom(SSLCustom.createSSLCustom(option.getSslCustomClass())); + return sslProperties; + } + + private String getStringProperty(Environment environment, String defaultValue, String... keys) { + for (String key : keys) { + if (environment.getProperty(key) != null) { + return environment.getProperty(key); + } + } + return defaultValue; + } + + private boolean getBooleanProperty(Environment environment, boolean defaultValue, String... keys) { + for (String key : keys) { + if (environment.getProperty(key) != null) { + return Boolean.parseBoolean(environment.getProperty(key)); + } + } + return defaultValue; + } + + private String getProjectName(Environment environment) { + return getStringProperty(environment, "default", PROJECT_KEY); + } + + private List getRBACAddressList(Environment environment) { + String address = environment.getProperty(RBAC_ADDRESS, "http://127.0.0.1:30100"); + return Arrays.asList(address.split(",")); + } + + + public static Boolean isProxyEnable(Environment environment) { + return environment.getProperty(VertxConst.PROXY_ENABLE, boolean.class, false); + } + + public static String getProxyHost(Environment environment) { + return environment.getProperty(VertxConst.PROXY_HOST, "127.0.0.1"); + } + + public static int getProxyPort(Environment environment) { + return environment.getProperty(VertxConst.PROXY_PORT, int.class, 8080); + } + + public static String getProxyUsername(Environment environment) { + return environment.getProperty(VertxConst.PROXY_USERNAME); + } + + public static String getProxyPasswd(Environment environment) { + return environment.getProperty(VertxConst.PROXY_PASSWD); + } +} diff --git a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/ServiceStageConfiguration.java b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/ServiceStageConfiguration.java new file mode 100644 index 00000000000..5a369b70bf9 --- /dev/null +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/ServiceStageConfiguration.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.huaweicloud.servicestage; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ServiceStageConfiguration { + @Bean + public CasEnvVariablesAdapter casEnvVariablesAdapter() { + return new CasEnvVariablesAdapter(); + } +} diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/auth/TokenAuthHeaderProvider.java b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenAuthHeaderProvider.java similarity index 86% rename from service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/auth/TokenAuthHeaderProvider.java rename to huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenAuthHeaderProvider.java index 0f075568313..19bd550602b 100644 --- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/auth/TokenAuthHeaderProvider.java +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenAuthHeaderProvider.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.servicecomb.serviceregistry.auth; +package org.apache.servicecomb.huaweicloud.servicestage; import java.util.Collections; import java.util.HashMap; @@ -26,8 +26,8 @@ public class TokenAuthHeaderProvider implements AuthHeaderProvider { @Override - public Map authHeaders() { - String token = TokenCacheManager.getInstance().getToken(RBACBootStrapService.DEFAULT_REGISTRY_NAME); + public Map authHeaders(String host) { + String token = TokenCacheManager.getInstance().getToken(host); if (StringUtils.isEmpty(token)) { return new HashMap<>(); } diff --git a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenCacheManager.java b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenCacheManager.java new file mode 100644 index 00000000000..b4510bb4238 --- /dev/null +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenCacheManager.java @@ -0,0 +1,206 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.huaweicloud.servicestage; + +import java.net.URI; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.auth.Cipher; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.http.client.event.OperationEvents; +import org.apache.servicecomb.service.center.client.ServiceCenterClient; +import org.apache.servicecomb.service.center.client.model.RbacTokenRequest; +import org.apache.servicecomb.service.center.client.model.RbacTokenResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.eventbus.Subscribe; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + +import jakarta.ws.rs.core.Response.Status; + +public final class TokenCacheManager { + private static final Logger LOGGER = LoggerFactory.getLogger(TokenCacheManager.class); + + // special token used for special conditions + // e.g. un-authorized: will query token after token expired period + // e.g. not found: will query token after token expired period + public static final String INVALID_TOKEN = ""; + + private static final TokenCacheManager INSTANCE = new TokenCacheManager(); + + private Map serviceCenterClients; + + private TokenCache tokenCache; + + public static TokenCacheManager getInstance() { + return INSTANCE; + } + + private TokenCacheManager() { + } + + public void setServiceCenterClients(Map serviceCenterClients) { + this.serviceCenterClients = serviceCenterClients; + } + + public void addTokenCache(String registryName, String accountName, String password, Cipher cipher) { + tokenCache = new TokenCache(registryName, accountName, password, cipher); + } + + public String getToken(String host) { + if (tokenCache == null) { + return null; + } + return tokenCache.getToken(host); + } + + public class TokenCache { + private static final long TOKEN_REFRESH_TIME_IN_SECONDS = 20 * 60 * 1000; + + private final String registryName; + + private final String accountName; + + private final String password; + + private ExecutorService executorService; + + private LoadingCache cache; + + private final Cipher cipher; + + public TokenCache(String registryName, String accountName, String password, + Cipher cipher) { + this.registryName = registryName; + this.accountName = accountName; + this.password = password; + this.cipher = cipher; + + if (enabled()) { + executorService = Executors.newFixedThreadPool(1, t -> new Thread(t, "rbac-executor-" + this.registryName) { + @Override + public void run() { + try { + super.run(); + } catch (Throwable e) { + LOGGER.error("", e); + } + } + }); + cache = CacheBuilder.newBuilder() + .maximumSize(10) + .refreshAfterWrite(refreshTime(), TimeUnit.MILLISECONDS) + .build(new CacheLoader() { + @Override + public String load(String key) throws Exception { + return createHeaders(key); + } + + @Override + public ListenableFuture reload(String key, String oldValue) throws Exception { + return Futures.submit(() -> createHeaders(key), executorService); + } + }); + EventManager.getEventBus().register(this); + } + } + + @Subscribe + public void onNotPermittedEvent(OperationEvents.UnAuthorizedOperationEvent event) { + LOGGER.warn("address {} unAuthorized, refresh cache token!", event.getAddress()); + cache.refresh(getHostByAddress(event.getAddress())); + } + + private String getHostByAddress(String address) { + try { + URI uri = URI.create(address); + return uri.getHost(); + } catch (Exception e) { + LOGGER.error("get host by address [{}] error!", address, e); + return registryName; + } + } + + private String createHeaders(String host) { + LOGGER.info("start to create RBAC headers for host: {}", host); + + ServiceCenterClient serviceCenterClient = serviceCenterClients.get(this.registryName); + + RbacTokenRequest request = new RbacTokenRequest(); + request.setName(accountName); + request.setPassword(new String(cipher.decrypt(password.toCharArray()))); + + RbacTokenResponse rbacTokenResponse = serviceCenterClient.queryToken(request, host); + + if (Status.UNAUTHORIZED.getStatusCode() == rbacTokenResponse.getStatusCode() + || Status.FORBIDDEN.getStatusCode() == rbacTokenResponse.getStatusCode()) { + // password wrong, do not try anymore + LOGGER.warn("username or password may be wrong, stop trying to query tokens."); + return INVALID_TOKEN; + } + if (Status.NOT_FOUND.getStatusCode() == rbacTokenResponse.getStatusCode()) { + // service center not support, do not try + LOGGER.warn("service center do not support RBAC token, you should not config account info"); + return INVALID_TOKEN; + } + if (Status.INTERNAL_SERVER_ERROR.getStatusCode() == rbacTokenResponse.getStatusCode()) { + // return null for server_error, so the token information can be re-fetched on the next call. + // It will prompt 'CacheLoader returned null for key xxx' + LOGGER.warn("service center query RBAC token error!"); + return null; + } + + LOGGER.info("refresh host [{}] token successfully {}", host, rbacTokenResponse.getStatusCode()); + return rbacTokenResponse.getToken(); + } + + protected long refreshTime() { + return TOKEN_REFRESH_TIME_IN_SECONDS; + } + + public String getToken(String host) { + if (!enabled()) { + return null; + } + String address = host; + if (StringUtils.isEmpty(address)) { + address = registryName; + } + try { + return cache.get(address); + } catch (Exception e) { + LOGGER.error("failed to create token", e); + return null; + } + } + + private boolean enabled() { + return !StringUtils.isEmpty(this.accountName) + && !StringUtils.isEmpty(this.password); + } + } +} diff --git a/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.AuthHeaderProvider b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.AuthHeaderProvider index e756f8985d0..a78d2c2e33b 100644 --- a/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.AuthHeaderProvider +++ b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.AuthHeaderProvider @@ -16,3 +16,4 @@ # org.apache.servicecomb.huaweicloud.servicestage.AKSKAuthHeaderProvider +org.apache.servicecomb.huaweicloud.servicestage.TokenAuthHeaderProvider diff --git a/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.Cipher b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.Cipher index e242e7a854a..8d0efa25aef 100644 --- a/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.Cipher +++ b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.Cipher @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.foundation.auth.ShaAKSKCipher \ No newline at end of file +org.apache.servicecomb.foundation.auth.ShaAKSKCipher diff --git a/service-registry/registry-service-center/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.bootstrap.BootStrapService b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.bootstrap.BootStrapService similarity index 84% rename from service-registry/registry-service-center/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.bootstrap.BootStrapService rename to huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.bootstrap.BootStrapService index e515f0a1aa6..90c339ba065 100644 --- a/service-registry/registry-service-center/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.bootstrap.BootStrapService +++ b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.bootstrap.BootStrapService @@ -15,4 +15,5 @@ # limitations under the License. # -org.apache.servicecomb.serviceregistry.auth.RBACBootStrapService \ No newline at end of file +org.apache.servicecomb.huaweicloud.servicestage.RBACBootStrapService +org.apache.servicecomb.huaweicloud.servicestage.AKSKAuthHeaderProvider diff --git a/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.adapter.EnvAdapter b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.adapter.EnvAdapter index a7eb97b3bc3..2aa5b486121 100644 --- a/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.adapter.EnvAdapter +++ b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.adapter.EnvAdapter @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.huaweicloud.servicestage.CasEnvVariablesAdapter \ No newline at end of file +org.apache.servicecomb.huaweicloud.servicestage.CasEnvVariablesAdapter diff --git a/huawei-cloud/servicestage/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/huawei-cloud/servicestage/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..04b38f05d6f --- /dev/null +++ b/huawei-cloud/servicestage/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## 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.servicecomb.huaweicloud.servicestage.ServiceStageConfiguration diff --git a/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestAKSKAuthHeaderProvider.java b/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestAKSKAuthHeaderProvider.java index d5ff9cdda38..2b82672c77c 100644 --- a/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestAKSKAuthHeaderProvider.java +++ b/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestAKSKAuthHeaderProvider.java @@ -20,33 +20,43 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; - -import com.netflix.config.ConcurrentCompositeConfiguration; +import org.springframework.core.env.Environment; public class TestAKSKAuthHeaderProvider { @Test public void test_project_name_properly_encoded_en() { - ConcurrentCompositeConfiguration configuration = Mockito.mock(ConcurrentCompositeConfiguration.class); - Mockito.when(configuration.getBoolean("servicecomb.credentials.akskEnabled", true)).thenReturn(true); - Mockito.when(configuration.getString("servicecomb.credentials.project", "default")).thenReturn("hello"); - Mockito.when(configuration.getString("servicecomb.credentials.accessKey", "")).thenReturn("access key"); - Mockito.when(configuration.getString("servicecomb.credentials.secretKey", "")).thenReturn("secret key"); - Mockito.when(configuration.getString("servicecomb.credentials.akskCustomCipher", "default")).thenReturn("default"); + Environment environment = Mockito.mock(Environment.class); + Mockito.when(environment.getProperty("servicecomb.credentials.akskEnabled", + boolean.class, true)).thenReturn(true); + Mockito.when(environment.getProperty("servicecomb.credentials.project", + "default")).thenReturn("hello"); + Mockito.when(environment.getProperty("servicecomb.credentials.accessKey", + "")).thenReturn("access key"); + Mockito.when(environment.getProperty("servicecomb.credentials.secretKey", + "")).thenReturn("secret key"); + Mockito.when(environment.getProperty("servicecomb.credentials.akskCustomCipher", + "default")).thenReturn("default"); - AKSKAuthHeaderProvider provider = new AKSKAuthHeaderProvider(configuration); + AKSKAuthHeaderProvider provider = new AKSKAuthHeaderProvider(); + provider.startup(environment); Assertions.assertEquals("hello", provider.authHeaders().get("X-Service-Project")); } @Test public void test_project_name_properly_encoded_cn() { - ConcurrentCompositeConfiguration configuration = Mockito.mock(ConcurrentCompositeConfiguration.class); - Mockito.when(configuration.getBoolean("servicecomb.credentials.akskEnabled", true)).thenReturn(true); - Mockito.when(configuration.getString("servicecomb.credentials.project", "default")).thenReturn("测试"); - Mockito.when(configuration.getString("servicecomb.credentials.accessKey", "")).thenReturn("access key"); - Mockito.when(configuration.getString("servicecomb.credentials.secretKey", "")).thenReturn("secret key"); - Mockito.when(configuration.getString("servicecomb.credentials.akskCustomCipher", "default")).thenReturn("default"); - - AKSKAuthHeaderProvider provider = new AKSKAuthHeaderProvider(configuration); + Environment environment = Mockito.mock(Environment.class); + Mockito.when(environment.getProperty("servicecomb.credentials.akskEnabled", + boolean.class, true)).thenReturn(true); + Mockito.when(environment.getProperty("servicecomb.credentials.project", + "default")).thenReturn("测试"); + Mockito.when(environment.getProperty("servicecomb.credentials.accessKey", + "")).thenReturn("access key"); + Mockito.when(environment.getProperty("servicecomb.credentials.secretKey", + "")).thenReturn("secret key"); + Mockito.when(environment.getProperty("servicecomb.credentials.akskCustomCipher", + "default")).thenReturn("default"); + AKSKAuthHeaderProvider provider = new AKSKAuthHeaderProvider(); + provider.startup(environment); Assertions.assertEquals("%E6%B5%8B%E8%AF%95", provider.authHeaders().get("X-Service-Project")); } } diff --git a/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestCasEnvConfig.java b/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestCasEnvConfig.java deleted file mode 100644 index 10ebf6f3949..00000000000 --- a/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestCasEnvConfig.java +++ /dev/null @@ -1,55 +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.servicecomb.huaweicloud.servicestage; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class TestCasEnvConfig { - - @BeforeAll - public static void init() { - System.setProperty("CAS_APPLICATION_ID", "application-id"); - System.setProperty("CAS_ENVIRONMENT_ID", "env-id"); - System.setProperty("SERVICECOMB_SERVICE_PROPS", "component:ConsumerService,other:A"); - System.setProperty("SERVICECOMB_INSTANCE_PROPS", "route:gray"); - } - - @Test - public void testConfig() { - CasEnvConfig instance = CasEnvConfig.INSTANCE; - Assertions.assertEquals(3, instance.getNonEmptyInstanceProperties().size()); - Assertions.assertEquals("application-id", instance.getNonEmptyInstanceProperties().get("CAS_APPLICATION_ID")); - Assertions.assertEquals("env-id", instance.getNonEmptyInstanceProperties().get("CAS_ENVIRONMENT_ID")); - Assertions.assertEquals("gray", instance.getNonEmptyInstanceProperties().get("route")); - - - Assertions.assertEquals(2, instance.getNonEmptyServiceProperties().size()); - Assertions.assertEquals("ConsumerService", instance.getNonEmptyServiceProperties().get("component")); - Assertions.assertEquals("A", instance.getNonEmptyServiceProperties().get("other")); - } - - @AfterAll - public static void destroy() { - System.getProperties().remove("CAS_ENVIRONMENT_ID"); - System.getProperties().remove("CAS_APPLICATION_ID"); - System.getProperties().remove("SERVICECOMB_SERVICE_PROPS"); - System.getProperties().remove("SERVICECOMB_INSTANCE_PROPS"); - } -} diff --git a/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestEnvVariablesAdapter.java b/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestEnvVariablesAdapter.java index ef0ce55f73a..5fc8bea722f 100644 --- a/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestEnvVariablesAdapter.java +++ b/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestEnvVariablesAdapter.java @@ -16,10 +16,7 @@ */ package org.apache.servicecomb.huaweicloud.servicestage; -import org.apache.servicecomb.registry.api.registry.Microservice; -import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -35,20 +32,21 @@ public static void init() { @Test public void testProcessInstance() { + // TODO: set CAS properties for service center CasEnvVariablesAdapter adapter = new CasEnvVariablesAdapter(); - MicroserviceInstance instance = new MicroserviceInstance(); - adapter.beforeRegisterInstance(instance); - - Assertions.assertEquals(3, instance.getProperties().size()); - Assertions.assertEquals("application-id", instance.getProperties().get("CAS_APPLICATION_ID")); - Assertions.assertEquals("env-id", instance.getProperties().get("CAS_ENVIRONMENT_ID")); - Assertions.assertEquals("gray", instance.getProperties().get("route")); - - Microservice microservice = new Microservice(); - adapter.beforeRegisterService(microservice); - Assertions.assertEquals(2, microservice.getProperties().size()); - Assertions.assertEquals("ConsumerService", microservice.getProperties().get("component")); - Assertions.assertEquals("A", microservice.getProperties().get("other")); +// MicroserviceInstance instance = new MicroserviceInstance(); +// adapter.beforeRegisterInstance(instance); +// +// Assertions.assertEquals(3, instance.getProperties().size()); +// Assertions.assertEquals("application-id", instance.getProperties().get("CAS_APPLICATION_ID")); +// Assertions.assertEquals("env-id", instance.getProperties().get("CAS_ENVIRONMENT_ID")); +// Assertions.assertEquals("gray", instance.getProperties().get("route")); +// +// Microservice microservice = new Microservice(); +// adapter.beforeRegisterService(microservice); +// Assertions.assertEquals(2, microservice.getProperties().size()); +// Assertions.assertEquals("ConsumerService", microservice.getProperties().get("component")); +// Assertions.assertEquals("A", microservice.getProperties().get("other")); } @AfterAll diff --git a/inspector/pom.xml b/inspector/pom.xml deleted file mode 100644 index 15ebe52f0d5..00000000000 --- a/inspector/pom.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - org.apache.servicecomb - java-chassis-parent - 3.0.0-SNAPSHOT - ../parents/default - - 4.0.0 - - inspector - Java Chassis::Inspector - - - - org.apache.servicecomb - transport-rest-vertx - - - org.apache.servicecomb - provider-jaxrs - - - org.apache.servicecomb - registry-local - test - - - org.apache.servicecomb - foundation-test-scaffolding - - - org.apache.logging.log4j - log4j-slf4j-impl - test - - - org.apache.logging.log4j - log4j-core - test - - - org.apache.servicecomb - transport-rest-servlet - test - - - org.mockito - mockito-inline - test - - - diff --git a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/InspectorBootListener.java b/inspector/src/main/java/org/apache/servicecomb/inspector/internal/InspectorBootListener.java deleted file mode 100644 index 3e654e5d991..00000000000 --- a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/InspectorBootListener.java +++ /dev/null @@ -1,57 +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.servicecomb.inspector.internal; - -import org.apache.servicecomb.config.priority.PriorityPropertyFactory; -import org.apache.servicecomb.core.BootListener; -import org.apache.servicecomb.registry.RegistrationManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class InspectorBootListener implements BootListener { - private static final Logger LOGGER = LoggerFactory.getLogger(InspectorBootListener.class); - - private final InspectorConfig inspectorConfig; - - private final PriorityPropertyFactory propertyFactory; - - public InspectorBootListener(InspectorConfig inspectorConfig, PriorityPropertyFactory propertyFactory) { - this.inspectorConfig = inspectorConfig; - this.propertyFactory = propertyFactory; - } - - @Override - public int getOrder() { - return Short.MAX_VALUE; - } - - @Override - public void onAfterTransport(BootEvent event) { - if (!inspectorConfig.isEnabled()) { - LOGGER.info("inspector is not enabled."); - return; - } - LOGGER.info("inspector is enabled."); - - // will not register this schemas to service registry - InspectorImpl inspector = new InspectorImpl() - .setPropertyFactory(propertyFactory) - .setSchemas(RegistrationManager.INSTANCE.getMicroservice().getSchemaMap()) - .correctBasePathForOnlineTest(event.getScbEngine()); - event.getScbEngine().getProducerProviderManager().registerSchema("inspector", inspector); - } -} diff --git a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/InspectorConfig.java b/inspector/src/main/java/org/apache/servicecomb/inspector/internal/InspectorConfig.java deleted file mode 100644 index 6aa6014b9c6..00000000000 --- a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/InspectorConfig.java +++ /dev/null @@ -1,35 +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.servicecomb.inspector.internal; - -import org.apache.servicecomb.config.inject.InjectProperties; -import org.apache.servicecomb.config.inject.InjectProperty; - -@InjectProperties(prefix = "servicecomb.inspector") -public class InspectorConfig { - @InjectProperty(keys = "enabled", defaultValue = "true") - private boolean enabled; - - public boolean isEnabled() { - return enabled; - } - - public InspectorConfig setEnabled(boolean enabled) { - this.enabled = enabled; - return this; - } -} diff --git a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/InspectorConfiguration.java b/inspector/src/main/java/org/apache/servicecomb/inspector/internal/InspectorConfiguration.java deleted file mode 100644 index afdfb3943bb..00000000000 --- a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/InspectorConfiguration.java +++ /dev/null @@ -1,35 +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.servicecomb.inspector.internal; - -import org.apache.servicecomb.config.priority.PriorityPropertyFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class InspectorConfiguration { - @Bean - public InspectorBootListener inspectorBootListener(InspectorConfig inspectorConfig, - PriorityPropertyFactory propertyFactory) { - return new InspectorBootListener(inspectorConfig, propertyFactory); - } - - @Bean - public InspectorConfig inspectorConfig() { - return new InspectorConfig(); - } -} diff --git a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/InspectorImpl.java b/inspector/src/main/java/org/apache/servicecomb/inspector/internal/InspectorImpl.java deleted file mode 100644 index 88581c4058b..00000000000 --- a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/InspectorImpl.java +++ /dev/null @@ -1,254 +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.servicecomb.inspector.internal; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - -import javax.servlet.http.Part; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response.Status; - -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.common.rest.resource.ClassPathStaticResourceHandler; -import org.apache.servicecomb.common.rest.resource.StaticResourceHandler; -import org.apache.servicecomb.config.ConfigUtil; -import org.apache.servicecomb.config.priority.PriorityProperty; -import org.apache.servicecomb.config.priority.PriorityPropertyFactory; -import org.apache.servicecomb.core.Const; -import org.apache.servicecomb.core.SCBEngine; -import org.apache.servicecomb.core.Transport; -import org.apache.servicecomb.foundation.common.part.InputStreamPart; -import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; -import org.apache.servicecomb.inspector.internal.model.DynamicPropertyView; -import org.apache.servicecomb.inspector.internal.model.PriorityPropertyView; -import org.apache.servicecomb.inspector.internal.swagger.SchemaFormat; -import org.apache.servicecomb.registry.RegistrationManager; -import org.apache.servicecomb.registry.definition.DefinitionConst; -import org.apache.servicecomb.swagger.SwaggerUtils; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.annotations.VisibleForTesting; -import com.netflix.config.DynamicProperty; - -import io.swagger.annotations.ApiResponse; -import io.swagger.models.Swagger; - -@Path("/inspector") -public class InspectorImpl { - private static final Logger LOGGER = LoggerFactory.getLogger(InspectorImpl.class); - - private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - - private final StaticResourceHandler resourceHandler = new ClassPathStaticResourceHandler(); - - private Map schemas; - - private PriorityPropertyFactory propertyFactory; - - public InspectorImpl setPropertyFactory(PriorityPropertyFactory propertyFactory) { - this.propertyFactory = propertyFactory; - return this; - } - - @VisibleForTesting - public Map getSchemas() { - return schemas; - } - - public InspectorImpl setSchemas(Map schemas) { - this.schemas = schemas; - return this; - } - - // when work in servlet mode, should concat url prefix - // otherwise swagger ide can not run online test - // - // ServiceComb consumer has not this problem - // ServiceComb consumer not care for producer deploy with or without servlet - public InspectorImpl correctBasePathForOnlineTest(SCBEngine scbEngine) { - Transport restTransport = scbEngine.getTransportManager().findTransport(Const.RESTFUL); - if (restTransport == null || - !restTransport.getClass().getName() - .equals("org.apache.servicecomb.transport.rest.servlet.ServletRestTransport")) { - return this; - } - - String urlPrefix = ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX); - if (StringUtils.isEmpty(urlPrefix)) { - return this; - } - - for (Entry entry : schemas.entrySet()) { - Swagger swagger = SwaggerUtils.parseSwagger(entry.getValue()); - if (swagger.getBasePath().startsWith(urlPrefix)) { - continue; - } - - swagger.setBasePath(urlPrefix + swagger.getBasePath()); - - entry.setValue(SwaggerUtils.swaggerToString(swagger)); - } - return this; - } - - @Path("/schemas") - @GET - public Collection getSchemaIds() { - return schemas.keySet(); - } - - @Path("/download/schemas") - @GET - @ApiResponse(code = 200, message = "", response = File.class) - public Response downloadSchemas(@QueryParam("format") SchemaFormat format) { - if (format == null) { - format = SchemaFormat.SWAGGER; - } - - // normally, schema will not be too big, just save them in memory temporarily - ByteArrayOutputStream os = new ByteArrayOutputStream(); - try (ZipOutputStream zos = new ZipOutputStream(os)) { - for (Entry entry : schemas.entrySet()) { - // begin writing a new ZIP entry, positions the stream to the start of the entry data - zos.putNextEntry(new ZipEntry(entry.getKey() + format.getSuffix())); - - String content = entry.getValue(); - zos.write(content.getBytes(StandardCharsets.UTF_8)); - zos.closeEntry(); - } - } catch (Throwable e) { - String msg = "failed to create schemas zip file, format=" + format + "."; - LOGGER.error(msg, e); - return Response.failResp(new InvocationException(Status.INTERNAL_SERVER_ERROR, msg)); - } - - Part part = new InputStreamPart(null, new ByteArrayInputStream(os.toByteArray())) - .setSubmittedFileName( - RegistrationManager.INSTANCE.getMicroservice().getServiceName() + format.getSuffix() + ".zip"); - return Response.ok(part); - } - - @Path("/schemas/{schemaId}") - @GET - @ApiResponse(code = 200, message = "", response = File.class) - public Response getSchemaContentById(@PathParam("schemaId") String schemaId, - @QueryParam("format") SchemaFormat format, @QueryParam("download") boolean download) { - String swaggerContent = schemas.get(schemaId); - if (swaggerContent == null) { - return Response.failResp(new InvocationException(Status.NOT_FOUND, Status.NOT_FOUND.getReasonPhrase())); - } - - if (format == null) { - format = SchemaFormat.SWAGGER; - } - - byte[] bytes = swaggerContent.getBytes(StandardCharsets.UTF_8); - - Part part = new InputStreamPart(null, new ByteArrayInputStream(bytes)) - .setSubmittedFileName(schemaId + format.getSuffix()); - - Response response = Response.ok(part); - if (!download) { - response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "inline"); - } - response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_HTML); - return response; - } - - @Path("/{path : .+}") - @GET - @ApiResponse(code = 200, message = "", response = File.class) - public Response getStaticResource(@PathParam("path") String path) { - return resourceHandler.handle(path); - } - - @Path("/dynamicProperties") - @GET - public List dynamicProperties() { - List views = new ArrayList<>(); - for (DynamicProperty property : ConfigUtil.getAllDynamicProperties().values()) { - views.add(createDynamicPropertyView(property)); - } - - // show more callback first, because maybe there is memory leak problem - // show recently changed second - // and sort by key - views.sort(Comparator - .comparing(DynamicPropertyView::getCallbackCount) - .thenComparing(DynamicPropertyView::getChangedTime).reversed() - .thenComparing(DynamicPropertyView::getKey)); - return views; - } - - private DynamicPropertyView createDynamicPropertyView(DynamicProperty property) { - DynamicPropertyView view = new DynamicPropertyView(); - view.setKey(property.getName()); - view.setValue(property.getString()); - - if (property.getChangedTimestamp() != 0) { - LocalDateTime localDatetime = LocalDateTime - .ofInstant(Instant.ofEpochMilli(property.getChangedTimestamp()), ZoneId.systemDefault()); - view.setChangedTime(localDatetime.format(FORMATTER)); - } - - view.setCallbackCount(ConfigUtil.getCallbacks(property).size()); - return view; - } - - @Path("/priorityProperties") - @GET - public List priorityProperties() { - List views = new ArrayList<>(); - propertyFactory.getProperties() - .forEach(p -> views.add(createPriorityPropertyView(p))); - return views; - } - - private PriorityPropertyView createPriorityPropertyView(PriorityProperty priorityProperty) { - PriorityPropertyView view = new PriorityPropertyView(); - view.setDynamicProperties(new ArrayList<>()); - for (DynamicProperty property : priorityProperty.getProperties()) { - view.getDynamicProperties().add(createDynamicPropertyView(property)); - } - view.setDefaultValue(String.valueOf(priorityProperty.getDefaultValue())); - view.setValue(String.valueOf(priorityProperty.getValue())); - return view; - } -} diff --git a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/model/DynamicPropertyView.java b/inspector/src/main/java/org/apache/servicecomb/inspector/internal/model/DynamicPropertyView.java deleted file mode 100644 index bf7c68fc7cc..00000000000 --- a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/model/DynamicPropertyView.java +++ /dev/null @@ -1,59 +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.servicecomb.inspector.internal.model; - -public class DynamicPropertyView { - private String key; - - private String value; - - private String changedTime = ""; - - private int callbackCount; - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public String getChangedTime() { - return changedTime; - } - - public void setChangedTime(String changedTime) { - this.changedTime = changedTime; - } - - public int getCallbackCount() { - return callbackCount; - } - - public void setCallbackCount(int callbackCount) { - this.callbackCount = callbackCount; - } -} diff --git a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/model/PriorityPropertyView.java b/inspector/src/main/java/org/apache/servicecomb/inspector/internal/model/PriorityPropertyView.java deleted file mode 100644 index d1524946d8f..00000000000 --- a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/model/PriorityPropertyView.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.servicecomb.inspector.internal.model; - -import java.util.List; - -public class PriorityPropertyView { - private List dynamicProperties; - - private String defaultValue; - - private String value; - - public List getDynamicProperties() { - return dynamicProperties; - } - - public void setDynamicProperties( - List dynamicProperties) { - this.dynamicProperties = dynamicProperties; - } - - public String getDefaultValue() { - return defaultValue; - } - - public void setDefaultValue(String defaultValue) { - this.defaultValue = defaultValue; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } -} diff --git a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/swagger/SchemaFormat.java b/inspector/src/main/java/org/apache/servicecomb/inspector/internal/swagger/SchemaFormat.java deleted file mode 100644 index 099350c3d3f..00000000000 --- a/inspector/src/main/java/org/apache/servicecomb/inspector/internal/swagger/SchemaFormat.java +++ /dev/null @@ -1,32 +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.servicecomb.inspector.internal.swagger; - -public enum SchemaFormat { - SWAGGER(".yaml"), - HTML(".html"); - - private final String suffix; - - SchemaFormat(String suffix) { - this.suffix = suffix; - } - - public String getSuffix() { - return suffix; - } -} diff --git a/inspector/src/main/resources/META-INF/spring.factories b/inspector/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 2099d563a15..00000000000 --- a/inspector/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,18 +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. -# -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.servicecomb.inspector.internal.InspectorConfiguration diff --git a/inspector/src/main/resources/webroot/index.html b/inspector/src/main/resources/webroot/index.html deleted file mode 100644 index 29b3ccb9353..00000000000 --- a/inspector/src/main/resources/webroot/index.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - ServiceComb inspector - - - - - - - - - - - - - -

- - - - - - configs - - - - - dynamic properties - - - - - priority properties - - - - - - - - schemas - -
-
-
-
-
-
- -
- - - - \ No newline at end of file diff --git a/inspector/src/main/resources/webroot/static/css/main.css b/inspector/src/main/resources/webroot/static/css/main.css deleted file mode 100644 index 17cd2f6e18f..00000000000 --- a/inspector/src/main/resources/webroot/static/css/main.css +++ /dev/null @@ -1,93 +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. - */ - -* { - box-sizing: border-box; -} - -html, -body { - width: 100%; - height: 100%; - margin: 0px; - display: flex; -} - -.left { - flex-direction: column; - flex-shrink: 0; - width: 300px; - height: 100%; - overflow-y: scroll; -} - -.content { - overflow: hidden; - position: relative; - height: 100%; - width: 100%; -} - -.content iframe { - border: 0; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; -} - -#scbLogo { - margin: auto; - display: block; - width: 50%; -} - -.table-container { - width: 100%; - margin: 10px; - overflow-y: scroll; -} - -.table { - border-spacing: 0px; - width: 100%; - word-wrap: break-word; - word-break: break-all; -} - -.table.left-head th { - text-align: left; - white-space: nowrap; -} - -.table tr:nth-of-type(even) { - background: #f8f8f7 -} - -.table.fix-head thead th { - position: sticky; - position: -webkit-sticky; - top: 0; - z-index: 999; - background: #f7f8f7; -} - -.table th, .table td { - border: 1px solid #dedede; - padding: 8px; -} \ No newline at end of file diff --git a/inspector/src/main/resources/webroot/static/dynamicProperties.html b/inspector/src/main/resources/webroot/static/dynamicProperties.html deleted file mode 100644 index 04e60904494..00000000000 --- a/inspector/src/main/resources/webroot/static/dynamicProperties.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - -
- - - - - - - - - - - -
indexkeyvaluechanged timecallback count
-
- - - - \ No newline at end of file diff --git a/inspector/src/main/resources/webroot/static/images/ServiceComb-logo-3.jpg b/inspector/src/main/resources/webroot/static/images/ServiceComb-logo-3.jpg deleted file mode 100644 index b2cb70678ce..00000000000 Binary files a/inspector/src/main/resources/webroot/static/images/ServiceComb-logo-3.jpg and /dev/null differ diff --git a/inspector/src/main/resources/webroot/static/images/download-all.png b/inspector/src/main/resources/webroot/static/images/download-all.png deleted file mode 100644 index 97637b3c507..00000000000 Binary files a/inspector/src/main/resources/webroot/static/images/download-all.png and /dev/null differ diff --git a/inspector/src/main/resources/webroot/static/images/download.png b/inspector/src/main/resources/webroot/static/images/download.png deleted file mode 100644 index b898f9d8adb..00000000000 Binary files a/inspector/src/main/resources/webroot/static/images/download.png and /dev/null differ diff --git a/inspector/src/main/resources/webroot/static/js/main.js b/inspector/src/main/resources/webroot/static/js/main.js deleted file mode 100644 index e68ff58cf6c..00000000000 --- a/inspector/src/main/resources/webroot/static/js/main.js +++ /dev/null @@ -1,38 +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. - */ - -function ajaxGet(url, succCallback) { - var xmlhttp = new XMLHttpRequest(); - xmlhttp.onreadystatechange = function () { - if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { - succCallback(xmlhttp.responseText); - } - } - xmlhttp.open("GET", url, true); - xmlhttp.send(); -} - -function ajaxJsonGet(url, succCallback) { - ajaxGet(url, function (text) { - succCallback(JSON.parse(text)) - }); -} - -function getBlobURL(code, type) { - var blob = new Blob([code], {type: type}) - return URL.createObjectURL(blob) -} \ No newline at end of file diff --git a/inspector/src/main/resources/webroot/static/priorityProperties.html b/inspector/src/main/resources/webroot/static/priorityProperties.html deleted file mode 100644 index e816fcd3e13..00000000000 --- a/inspector/src/main/resources/webroot/static/priorityProperties.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - -
- - - - - - - - - - - - - -
indexvaluedefault valuekeyvaluechanged timecallback count
-
- - - - \ No newline at end of file diff --git a/inspector/src/main/resources/webroot/static/schemas/schemas.css b/inspector/src/main/resources/webroot/static/schemas/schemas.css deleted file mode 100644 index b8905caf06e..00000000000 --- a/inspector/src/main/resources/webroot/static/schemas/schemas.css +++ /dev/null @@ -1,37 +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. - */ - -#schema-format { - margin-right: 4px; - width: 80px; - flex-shrink: 0; -} - -#schemas .download { - background-image: url(../images/download.png); - background-size: 100% 100%; - width: 15px; - height: 18px; - flex-shrink: 0; - margin-right: 3px; - align-self: center; - cursor: pointer; -} - -#schemas .download.download-all { - background-image: url(../images/download-all.png); -} \ No newline at end of file diff --git a/inspector/src/main/resources/webroot/static/schemas/schemas.js b/inspector/src/main/resources/webroot/static/schemas/schemas.js deleted file mode 100644 index 788428cb0f4..00000000000 --- a/inspector/src/main/resources/webroot/static/schemas/schemas.js +++ /dev/null @@ -1,111 +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. - */ - -function initSchemas() { - var eleFormat = document.getElementById("schema-format"); - eleFormat.onchange = function () { - localStorage.setItem("schemaFormatIdx", eleFormat.selectedIndex); - if (mainTree.config.activeNodeId != null && mainTree.config.activeNodeId.startsWith("schemas://")) { - mainTree.clickActiveTreeNode(); - } - }; - var formatIdx = localStorage.getItem("schemaFormatIdx"); - if (formatIdx != null) { - eleFormat.selectedIndex = formatIdx; - } - - ajaxJsonGet("schemas", initSchemasTree); -} - -function initSchemasTree(schemas) { - schemas = schemas.sort(); - var eleSchemas = document.getElementById("schemas"); - var childrenContent = ""; - for (var idx in schemas) { - childrenContent += '' + - ' ' + - ' ' + schemas[idx] + '' + - '
' + - '
' + - '
'; - } - childrenContent += '
'; - eleSchemas.insertAdjacentHTML("beforeend", childrenContent); - - mainTree.init(); -} - -function fetchAndshowSchemaAsSwagger(url) { - ajaxGet(url, function (schema) { - frame.editor.specActions.updateSpec(schema); - }); -} - -function showSchemaAsSwagger(url) { - if (frame.SwaggerEditorBundle != null) { - // reuse swagger editor - fetchAndshowSchemaAsSwagger(url); - return; - } - - ajaxGet(url, function (schema) { - localStorage.setItem("swagger-editor-content", schema); - }); - // this cdn return html as text/plain, so we must let iframe load it as html - ajaxGet("https://cdn.jsdelivr.net/npm/swagger-editor-dist@3.6.24/index.html", - function (html) { - html = html.replace('